Mir

Merge lp:~mir-team/mir/trusted_sessions into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: kevin gunn
Approved revision: no longer in the source branch.
Merged at revision: 1701
Proposed branch: lp:~mir-team/mir/trusted_sessions
Merge into: lp:mir
Diff against target: 6122 lines (+4349/-363)
87 files modified
examples/CMakeLists.txt (+6/-2)
examples/demo-inprocess-surface-client/inprocess_egl_client.cpp (+1/-1)
examples/prompt_session.c (+231/-0)
include/client/mir_toolkit/mir_connection.h (+0/-20)
include/client/mir_toolkit/mir_prompt_session.h (+87/-0)
include/server/mir/default_server_configuration.h (+6/-0)
include/server/mir/frontend/prompt_session.h (+48/-0)
include/server/mir/frontend/session_mediator_report.h (+9/-1)
include/server/mir/frontend/shell.h (+10/-0)
include/server/mir/scene/null_prompt_session_listener.h (+40/-0)
include/server/mir/scene/prompt_session.h (+37/-0)
include/server/mir/scene/prompt_session_creation_parameters.h (+36/-0)
include/server/mir/scene/prompt_session_listener.h (+52/-0)
include/server/mir/scene/prompt_session_manager.h (+110/-0)
include/server/mir/scene/session.h (+3/-0)
include/server/mir/shell/session_coordinator_wrapper.h (+14/-0)
include/shared/mir_toolkit/client_types.h (+28/-2)
include/shared/mir_toolkit/common.h (+6/-0)
include/shared/mir_toolkit/event.h (+10/-1)
include/test/mir_test/test_protobuf_client.h (+17/-0)
include/test/mir_test_doubles/mock_prompt_session_listener.h (+48/-0)
include/test/mir_test_doubles/mock_scene_session.h (+3/-0)
include/test/mir_test_doubles/mock_shell.h (+12/-0)
include/test/mir_test_doubles/null_client_event_sink.h (+42/-0)
include/test/mir_test_doubles/null_prompt_session.h (+37/-0)
include/test/mir_test_doubles/null_prompt_session_manager.h (+82/-0)
include/test/mir_test_doubles/stub_scene_session.h (+14/-1)
include/test/mir_test_doubles/stub_shell.h (+19/-0)
src/client/CMakeLists.txt (+4/-1)
src/client/connection_configuration.h (+4/-0)
src/client/default_connection_configuration.cpp (+20/-1)
src/client/default_connection_configuration.h (+4/-0)
src/client/event_distributor.h (+37/-0)
src/client/event_handler_register.h (+47/-0)
src/client/event_sink.h (+44/-0)
src/client/mir_connection.cpp (+9/-36)
src/client/mir_connection.h (+5/-8)
src/client/mir_connection_api.cpp (+0/-18)
src/client/mir_event_distributor.cpp (+56/-0)
src/client/mir_event_distributor.h (+43/-0)
src/client/mir_prompt_session.cpp (+203/-0)
src/client/mir_prompt_session.h (+94/-0)
src/client/mir_prompt_session_api.cpp (+118/-0)
src/client/rpc/make_rpc_channel.h (+3/-1)
src/client/rpc/make_socket_rpc_channel.cpp (+4/-3)
src/client/rpc/mir_socket_rpc_channel.cpp (+26/-7)
src/client/rpc/mir_socket_rpc_channel.h (+6/-2)
src/server/default_server_configuration.cpp (+11/-0)
src/server/frontend/protobuf_message_processor.cpp (+15/-3)
src/server/frontend/session_mediator.cpp (+91/-6)
src/server/frontend/session_mediator.h (+23/-6)
src/server/report/logging/session_mediator_report.cpp (+15/-0)
src/server/report/logging/session_mediator_report.h (+6/-0)
src/server/report/lttng/session_mediator_report.cpp (+11/-0)
src/server/report/lttng/session_mediator_report.h (+3/-0)
src/server/report/lttng/session_mediator_report_tp.h (+21/-0)
src/server/report/null/session_mediator_report.cpp (+12/-0)
src/server/report/null/session_mediator_report.h (+6/-0)
src/server/scene/CMakeLists.txt (+2/-0)
src/server/scene/application_session.cpp (+21/-0)
src/server/scene/application_session.h (+3/-0)
src/server/scene/default_configuration.cpp (+15/-1)
src/server/scene/prompt_session_container.cpp (+189/-0)
src/server/scene/prompt_session_container.h (+158/-0)
src/server/scene/prompt_session_manager_impl.cpp (+232/-0)
src/server/scene/prompt_session_manager_impl.h (+96/-0)
src/server/scene/session_manager.cpp (+47/-3)
src/server/scene/session_manager.h (+14/-2)
src/server/shell/session_coordinator_wrapper.cpp (+28/-2)
src/shared/protobuf/mir_protobuf.proto (+15/-4)
tests/acceptance-tests/CMakeLists.txt (+1/-1)
tests/acceptance-tests/test_nested_mir.cpp (+3/-0)
tests/acceptance-tests/test_prompt_session_client_api.cpp (+421/-0)
tests/acceptance-tests/test_trust_session_helper.cpp (+0/-218)
tests/integration-tests/frontend/test_application_mediator_report.cpp (+181/-0)
tests/integration-tests/test_session_manager.cpp (+3/-1)
tests/mir_test_doubles/test_protobuf_client.cpp (+27/-1)
tests/unit-tests/client/CMakeLists.txt (+2/-0)
tests/unit-tests/client/test_event_distributor.cpp (+152/-0)
tests/unit-tests/client/test_mir_prompt_session.cpp (+233/-0)
tests/unit-tests/frontend/stress_protobuf_communicator.cpp (+4/-1)
tests/unit-tests/frontend/test_session_mediator.cpp (+4/-4)
tests/unit-tests/scene/CMakeLists.txt (+2/-0)
tests/unit-tests/scene/test_application_session.cpp (+45/-0)
tests/unit-tests/scene/test_prompt_session_container.cpp (+273/-0)
tests/unit-tests/scene/test_prompt_session_manager.cpp (+217/-0)
tests/unit-tests/scene/test_session_manager.cpp (+12/-5)
To merge this branch: bzr merge lp:~mir-team/mir/trusted_sessions
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Abstain
Alexandros Frantzis (community) Approve
Cemil Azizoglu (community) Approve
Kevin DuBois (community) Needs Information
Daniel van Vugt Pending
Review via email: mp+221191@code.launchpad.net

This proposal supersedes a proposal from 2014-05-28.

Commit message

support for trusted prompt sessions: https://wiki.ubuntu.com/Security/TrustStoreAndSessions

Description of the change

support for trusted prompt sessions: https://wiki.ubuntu.com/Security/TrustStoreAndSessions

This branch is to try and shepherd code from lp:~nick-dedekind/mir/trusted_sessions towards landing

As a guide to understanding:

A "prompt sessions" is a tag used by a shell to associate the surfaces belonging to one application to another. The use case is to allow, e.g. pickers to embed surfaces into the client UI.

A scene "session" is a grouping of surfaces associated with a Mir connection.

This MP allows scene "sessions" to be associated with a "prompt session" either by the PID (for processes that are already running) or by the socket connection (for processes that have to be started). Each associated "session" will have the role "application", "helper" or "prompt provider"

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

The tests that fail shouldn't have been affected: triggering a rebuild.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

3072 + typedef enum {
3073 + HelperSession,
3074 + TrustedSession,
3075 + } TrustType;

http://unity.ubuntu.com/mir/cppguide/index.html?showone=Enumerator_Names#Enumerator_Names

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

[ FAILED ] 9 tests, listed below:
[ FAILED ] TestClientInput.clients_receive_key_input
[ FAILED ] TestClientInput.clients_receive_us_english_mapped_keys
[ FAILED ] TestClientInput.clients_receive_motion_inside_window
[ FAILED ] TestClientInput.clients_receive_button_events_inside_window
[ FAILED ] TestClientInput.multiple_clients_receive_motion_inside_windows
[ FAILED ] TestClientInput.clients_do_not_receive_motion_outside_input_region
[ FAILED ] TestClientInput.scene_obscure_motion_events_by_stacking
[ FAILED ] TestClientInput.hidden_clients_do_not_receive_pointer_events
[ FAILED ] TestClientInput.clients_receive_motion_within_co_ordinate_system_of_window

I still don't see how these tests are broken by this MP (and just on g++/utopic/armhf too!)

But it's Sunday so I'm leaving the tests running (on my trusty laptop) to see if I can reproduce while I go for a walk.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

A different (probably unrelated) failure.

[----------] 5 tests from ServerShutdown/OnSignal (1604 ms total)

[----------] Global test environment tear-down
[==========] 92 tests from 22 test cases ran. (184934 ms total)
[ PASSED ] 92 tests.

  YOU HAVE 11 DISABLED TESTS

==12239==
==12239== HEAP SUMMARY:
==12239== in use at exit: 4,359 bytes in 18 blocks
==12239== total heap usage: 233,099 allocs, 233,081 frees, 12,409,967 bytes allocated
==12239==
==12239== 744 (120 direct, 624 indirect) bytes in 1 blocks are definitely lost in loss record 15 of 17
==12239== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12239== by 0x56FD701: std::_Function_handler<std::shared_ptr<mir::client::EventDistributor> (), mir::client::DefaultConnectionConfiguration::the_event_distributor()::{lambda()#1}>::_M_invoke(std::_Any_data const&) (new_allocator.h:104)
==12239== by 0x56FDCD6: mir::client::DefaultConnectionConfiguration::the_event_distributor() (functional:2471)
==12239== by 0x57012CF: std::_Function_handler<std::shared_ptr<google::protobuf::RpcChannel> (), mir::client::DefaultConnectionConfiguration::the_rpc_channel()::{lambda()#1}>::_M_invoke(std::_Any_data const&) (default_connection_configuration.cpp:86)
==12239== by 0x56FFE86: mir::client::DefaultConnectionConfiguration::the_rpc_channel() (functional:2471)
==12239== by 0x56E30BC: MirConnection::MirConnection(mir::client::ConnectionConfiguration&) (mir_connection.cpp:87)
==12239== by 0x56E5AE5: (anonymous namespace)::mir_default_connect(char const*, char const*, void (*)(MirConnection*, void*), void*) (mir_connection_api.cpp:76)
==12239== by 0x56E6484: mir_connect (mir_connection_api.cpp:133)
==12239== by 0x56E64F5: mir_connect_sync (mir_connection_api.cpp:149)
==12239== by 0x682752: mir_test_framework::BasicClientServerFixture<mir_test_framework::StubbedServerConfiguration>::SetUp() (basic_client_server_fixture.h:40)
==12239== by 0x832042: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2078)
==12239== by 0x8251BD: testing::Test::Run() (gtest.cc:2146)
==12239==
==12239== LEAK SUMMARY:
==12239== definitely lost: 120 bytes in 1 blocks
==12239== indirectly lost: 624 bytes in 3 blocks
==12239== possibly lost: 34 bytes in 1 blocks
==12239== still reachable: 3,581 bytes in 13 blocks
==12239== suppressed: 0 bytes in 0 blocks
==12239== Reachable blocks (those to which a pointer was found) are not shown.
==12239== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==12239==
==12239== For counts of detected and suppressed errors, rerun with: -v
==12239== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

I'm going to resubmit with the pre-req on lp:~alan-griffiths/mir/remove-process-compexity-from-TestClientInput to reduce the diff while I look into this.

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote : Posted in a previous version of this proposal

First run - not understanding the details completely yet - but here are some nits from the first read:

+ 1163 authored by Alexandros?

+ 1678 , 3314 egyptian style braces

+ 1763 std::string error not used
+ 1764ff why not just call set_state( session.has_error() ? ... )

+ 3160, 3122 style guide

+ 2901 ", "
+ 2991,3012 tsit->second should be easier to

+ 2881 uint?

+ 3294 why the class scope

+ 3495 add_add?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

OK, tracked down the leaky test...

$ valgrind bin/mir_acceptance_tests --gtest_filter=TrustSessionClientAPI.can_start_and_stop_a_trust_session
==10781== Memcheck, a memory error detector
==10781== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==10781== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==10781== Command: bin/mir_acceptance_tests --gtest_filter=TrustSessionClientAPI.can_start_and_stop_a_trust_session
==10781==
Running main() from command_line_server_configuration.cpp
Note: Google Test filter = TrustSessionClientAPI.can_start_and_stop_a_trust_session
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from TrustSessionClientAPI
[ RUN ] TrustSessionClientAPI.can_start_and_stop_a_trust_session
[ OK ] TrustSessionClientAPI.can_start_and_stop_a_trust_session (1106 ms)
[----------] 1 test from TrustSessionClientAPI (1120 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1168 ms total)
[ PASSED ] 1 test.
==10781==
==10781== HEAP SUMMARY:
==10781== in use at exit: 4,359 bytes in 18 blocks
==10781== total heap usage: 7,096 allocs, 7,078 frees, 454,206 bytes allocated
==10781==
==10781== LEAK SUMMARY:
==10781== definitely lost: 120 bytes in 1 blocks

review: Needs Fixing
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

343 + * \param [in] start_callback The function to be called when the trust session event occurs

s/event_callback/
s/the trusted session event/a trusted session event/ ?

338 + * Create an new trust session
347 +MirWaitHandle *mir_connection_start_trust_session(MirConnection* connection,

Do we 'create' a new trust session or 'start' it? (also see the last note in this comment).

376 +MirWaitHandle *mir_trust_session_add_trusted_session(MirTrustSession *trust_session,
377 + pid_t session_pid,
378 + mir_trust_session_add_trusted_session_callback callback,
379 + void* context);
... and in other functions

Inconsistent '*' placement (void* vs MirTrustSession *trust_session).

379 + void* context);

Missing \param.

374 + * \return A MirTrustSessionAddTrustResult result of the call

Erroneous comment: function returns MirWaitHandle*

385 + * \return A MirTrustSessionAddTrustResult result of the call

Erroneous comment: function returns MirBool

390 +/**
391 + * Request the cancellation of a trust session
392 + * \param [in] trust_session The trust session
393 + * \return True if the request was made successfully,
394 + * false otherwise
395 + */

Missing \params, erroneous \return.

417 +void mir_trust_session_release(MirTrustSession* trust_session);

Why do we have 'release' as a separate step from 'stop'? I don't see a way to resume a trust session. I am probably missing some background information, but perhaps we can streamline the interfaces or at least improve the documentation.

We have 'start', 'stop' but also 'release', and no matching 'create'. This violates the principle of least surprise (it surprises me at least!). Perhaps create_and_start()?

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> + 1764ff why not just call set_state( session.has_error() ? ... )

I assume it is to avoid accessing session without a lock.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Just an amber light on this one; Mir 0.2.0 is already way oversized so we should not land something this size until that's out the way.

review: Needs Resubmitting
Revision history for this message
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal

> Just an amber light on this one; Mir 0.2.0 is already way oversized so we
> should not land something this size until that's out the way.

wait, if we treat devel as sacred, it shouldn't matter what the delta is. unless there is a verifiable lack of test/validation for this change?
This is a key item on our list for malta & i had requested that we actually hold off on 0.2.0 until we landed this.

Revision history for this message
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal

> Just an amber light on this one; Mir 0.2.0 is already way oversized so we
> should not land something this size until that's out the way.

wait, if we treat devel as sacred, it shouldn't matter what the delta is. unless there is a verifiable lack of test/validation for this change?
This is a key item on our list for malta & i had requested that we actually hold off on 0.2.0 until we landed this.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> Just an amber light on this one; Mir 0.2.0 is already way oversized so we
> should not land something this size until that's out the way.

Getting this reviewed, fixed and landed is a team goal for this week.

If you really feel there is benefit in landing both 0.2.0 and 0.2.1 then I won't argue. But the real problem is that you don't have confidence in our automated testing. Delaying landing this MP won't change that.

Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

> 343 + * \param [in] start_callback The function to be called when the trust
> session event occurs
>
> s/event_callback/
> s/the trusted session event/a trusted session event/ ?
>
> 338 + * Create an new trust session
> 347 +MirWaitHandle *mir_connection_start_trust_session(MirConnection*
> connection,
>
> Do we 'create' a new trust session or 'start' it? (also see the last note in
> this comment).
>
> 376 +MirWaitHandle *mir_trust_session_add_trusted_session(MirTrustSession
> *trust_session,
> 377 + pid_t session_pid,
> 378 + mir_trust_session_add_trusted_session_callback callback,
> 379 + void* context);
> ... and in other functions
>
> Inconsistent '*' placement (void* vs MirTrustSession *trust_session).
>
> 379 + void* context);
>
> Missing \param.
>
> 374 + * \return A MirTrustSessionAddTrustResult result of the call
>
> Erroneous comment: function returns MirWaitHandle*
>
> 385 + * \return A MirTrustSessionAddTrustResult result of the call
>
> Erroneous comment: function returns MirBool
>
> 390 +/**
> 391 + * Request the cancellation of a trust session
> 392 + * \param [in] trust_session The trust session
> 393 + * \return True if the request was made successfully,
> 394 + * false otherwise
> 395 + */
>
> Missing \params, erroneous \return.
>
> 417 +void mir_trust_session_release(MirTrustSession* trust_session);
>
> Why do we have 'release' as a separate step from 'stop'? I don't see a way to
> resume a trust session. I am probably missing some background information, but
> perhaps we can streamline the interfaces or at least improve the
> documentation.
>
> We have 'start', 'stop' but also 'release', and no matching 'create'. This
> violates the principle of least surprise (it surprises me at least!). Perhaps
> create_and_start()?

There was a separate create & start, but we removed the create because it was somewhat unnecessary. The reason I left the stop/release as separate methods was that we get asynchronous stop events from the server, so we need to be able to release without sending a stop.
I could change to create_and_start, but the same applies for mir_connect (not mir_create_connection and_connect ;)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Broken tests seem fixed

review: Abstain
Revision history for this message
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal

> > Just an amber light on this one; Mir 0.2.0 is already way oversized so we
> > should not land something this size until that's out the way.
>
> wait, if we treat devel as sacred, it shouldn't matter what the delta is.
> unless there is a verifiable lack of test/validation for this change?
> This is a key item on our list for malta & i had requested that we actually
> hold off on 0.2.0 until we landed this.

replying to myself - given that we are sacred on devel. we can certainly land 0.2.0 now, and just work to release this as part of 0.2.1 soon after.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

CI seems to have mislaid boost on amd64

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> There was a separate create & start, but we removed the create because it was
> somewhat unnecessary. The reason I left the stop/release as separate methods
> was that we get asynchronous stop events from the server, so we need to be
> able to release without sending a stop.
> I could change to create_and_start, but the same applies for mir_connect (not
> mir_create_connection and_connect ;)

[after some f2f discussion]

We can release in the API the object "automatically" after signaling the wait handle returned from the mir_trust_session_stop() call. This signal ought to be happen if the server responds or if the IPC fails. (I don't care about scenarios where the IPC succeeds and the server doesn't respond as there are bigger problems than the client potentially leaking an object.)

*s/Needs information/Needs discussion/*

review: Needs Information
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

358 + * \param [in] callback Callback function to be invoked when request
359 + * completes
361 + * \param [in] callback The function to be called when the trust session event occurs

Duplicate \param and wrong param name.

615 + NullTrustSessionListener() = default;
616 + virtual ~NullTrustSessionListener() noexcept(true) = default;
625 + NullTrustSessionListener(const NullTrustSessionListener&) = delete;
626 + NullTrustSessionListener& operator=(const NullTrustSessionListener&) = delete;

No need to re-default/re-delete special member functions in derived class.

916 +typedef void (*mir_trust_session_callback)(MirTrustSession* tps, void* context);

tps?

1747 +void MirTrustSession::register_trust_session_event_callback(
1748 + mir_trust_session_event_callback callback,
1749 + void* context)

1948 + auto trust_session = connection->create_trust_session();
1949 + if (event_callback)
1950 + trust_session->register_trust_session_event_callback(event_callback, context);
... and similar code

It seems like the callback information should be passed in the constructor of
MirTrustSession (and MirConnection::create_trust_session()))

1680 + if (handle_trust_session_event)
1681 + {
1682 + handle_trust_session_event(event.trust_session.new_state);
1683 + })

We could initialize handler_trust_session_event with a null handler (not a nullptr),
so we don't need to check if it is valid.

To be continued...

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> 358 + * \param [in] callback Callback function to be invoked when request
> 359 + * completes
> 361 + * \param [in] callback The function to be called when the trust session
> event occurs
>
> Duplicate \param and wrong param name.
>
> 615 + NullTrustSessionListener() = default;
> 616 + virtual ~NullTrustSessionListener() noexcept(true) = default;
> 625 + NullTrustSessionListener(const NullTrustSessionListener&) = delete;
> 626 + NullTrustSessionListener& operator=(const NullTrustSessionListener&) =
> delete;
>
> No need to re-default/re-delete special member functions in derived class.
>
> 916 +typedef void (*mir_trust_session_callback)(MirTrustSession* tps, void*
> context);
>
> tps?

fixed

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Resubmitted to remove prerequisite that creates spurious conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

1957 +MirTrustSession* mir_connection_create_trust_session(MirConnection* connection)

Not used.

3852 +TEST_F(TrustSessionClientAPI, can_start_and_stop_a_trust_session)

It seems like this test would benefit from mocks (instead of checking the
expectations manually).

2592 +void ms::ApplicationSession::begin_trust_session()
2602 +void ms::ApplicationSession::end_trust_session()

It would be nice to keep the terminology consistent (start/stop vs begin/end).

4964 +TEST_F(TrustSessionManager, successfully_adds_a_participant_by_pid)
... and similar tests

This test and other similar ones are just testing that the function doesn't
throw; they don't validate that a participant has been added. So, we should
either change the names or change the tests.

529 +class EventDistributor

We should avoid depending on concrete classes. Perhaps something like:

class EventHandlerRegister
{
    virtual void register_event_handler(...) = 0;
    virtual void unregister_event_handler(...) = 0;
};

class EventSink // perhaps reuse the class from the server?
{
    virtual void handle_event(...) = 0;

};

class EventDistributor : public EventHandlerRegister, EventSink {...impl...};

MirTrustSession(..., std::shared_ptr<mir::client::EventHandlerRegister> const&);
MirSocketRpcChannel(..., std::shared_ptr<mir::client::EventSink> const&)

To be continued...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> 3852 +TEST_F(TrustSessionClientAPI, can_start_and_stop_a_trust_session)
>
> It seems like this test would benefit from mocks (instead of checking the
> expectations manually).

Done

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> 1957 +MirTrustSession* mir_connection_create_trust_session(MirConnection*
> connection)
>
> Not used.
>
> 3852 +TEST_F(TrustSessionClientAPI, can_start_and_stop_a_trust_session)
>
> It seems like this test would benefit from mocks (instead of checking the
> expectations manually).

Alan has done this.

>
> 2592 +void ms::ApplicationSession::begin_trust_session()
> 2602 +void ms::ApplicationSession::end_trust_session()
>
> It would be nice to keep the terminology consistent (start/stop vs begin/end).

Changed:
ms::Session::begin_trust_session -> start_trust_session
ms::Session::end_trust_session -> end_trust_session
ms::TrustSessionListener::trusted_session_beginning -> trusted_participant_starting
ms::TrustSessionListener::trusted_session_ending -> trusted_participant_stopping

>
> 4964 +TEST_F(TrustSessionManager, successfully_adds_a_participant_by_pid)
> ... and similar tests
>
> This test and other similar ones are just testing that the function doesn't
> throw; they don't validate that a participant has been added. So, we should
> either change the names or change the tests.
>
> 529 +class EventDistributor
>
> We should avoid depending on concrete classes. Perhaps something like:
>
> class EventHandlerRegister
> {
> virtual void register_event_handler(...) = 0;
> virtual void unregister_event_handler(...) = 0;
> };
>
> class EventSink // perhaps reuse the class from the server?
> {
> virtual void handle_event(...) = 0;
>
> };
>
> class EventDistributor : public EventHandlerRegister, EventSink {...impl...};
>
> MirTrustSession(..., std::shared_ptr<mir::client::EventHandlerRegister>
> const&);
> MirSocketRpcChannel(..., std::shared_ptr<mir::client::EventSink> const&)
>
> To be continued...

Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

Sorry, mean to say "done with all those :)

ms::Session::begin_trust_session -> start_trust_session
ms::Session::end_trust_session -> end_trust_session
ms::TrustSessionListener::trusted_session_beginning -> trusted_participant_starting
ms::TrustSessionListener::trusted_session_ending -> trusted_participant_stopping

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

How does the trust session app (i.e. child in the example) know that the trusted session is established by the parent? In the example, this is not a problem, because of the sleep(1). If we remove it, things may not work. In general, shouldn't the child have a way to know this?

review: Needs Information
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

The child doesn't know anything about the fact it's running in a trust session. If the child's mir connection is established after the trust session starts, it will be added as a result of:

2852 + trust_session_manager->add_expected_session(new_session);

I used to call the scene::Session::start_trust_session method on all children added to the trust session, but there's no way for them to get the events without having a trust session object (We have no event distributor registration for mir connection yet). Until we have a concrete need for this, we decided to leave it out.

Revision history for this message
Alan Griffiths (alan-griffiths) :
review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) :
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> ms::TrustSessionListener::trusted_session_beginning ->
> trusted_participant_starting
> ms::TrustSessionListener::trusted_session_ending ->
> trusted_participant_stopping

How about:

s/trusted_participant_starting/participant_added/g
s/trusted_participant_stopping/participant_removed/g

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

I took a crack at a review. Forgive me if some of the questions are elementary; I haven't wrapped my head around everything that's going on. I guess needs info is the most appropriate review type.

Could you elaborate a bit more on why trust sessions need to be stopped and started?

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I took a crack at a review. Forgive me if some of the questions are
> elementary; I haven't wrapped my head around everything that's going on. I
> guess needs info is the most appropriate review type.

I've addressed a lot of the inline comments.

> Could you elaborate a bit more on why trust sessions need to be stopped and
> started?

This was discussed in Malta: Alexandros and I thought the two-phase teardown probably unnecessary, Nick thought there were synchronization issues with the client code when the (unity) server initiated a stop. We haven't reached consensus yet.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

1684 + std::lock_guard<std::recursive_mutex> thread_lock(thread_mutex);
1695 + std::lock_guard<std::recursive_mutex> thread_lock(thread_mutex);

Are these used to ensure that after returning from unregister() the corresponding handler will not be invoked (i.e. that it can't be in the process of being dispatched currently)?

1714 + for (int id : delete_later_ids)
1715 + event_handlers.erase(id);

We also need to clear delete_later_ids.

2131 + if (trust_session->get_state() == mir_trust_session_state_stopped)
2132 + mir_wait_for(trust_session->stop(null_callback, nullptr));

This doesn't look right (if trust session is stopped, then stop it?)

2765 + the_surface_coordinator(),
2766 + the_session_container(),
2767 + the_shell_focus_setter(),
...

Not indented properly.

2853 + auto old_focus = focus_application.lock();
2854 + if (old_focus == scene_session)
2855 + {
2856 + // only reset the focus if this session had focus
2857 + set_focus_to_locked(lock, app_container->successor_of(std::shared_ptr<Session>()));
2858 + }

How is this related to the trusted sessions functionality?

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

~~~~~
1771 + std::atomic<int> next_fn_id;
1772 + std::unordered_set<int> delete_later_ids;
1773 + std::atomic<bool> in_event;

do these have to be atomic? seems they could just be protected by the mutex. Also would be better to do without the recursive_mutex...

~~~~~

1760 + int register_event_handler(std::function<void(MirEvent const&)> const&) override;
1761 + void unregister_event_handler(int id) override;

unregister_event_handler only appears in the destructor of MirTrustSession, so could we have
1760 + RegisteredHandler register_event_handler(std::function<void(MirEvent const&)> const&) override;
and handle the unregistration via RAII?

~~~~
src/client/event_sink.h
seems very close to mf::EventSink, should the interface be shared between client and server?

~~~
Seems strange that we could call 'delete this' in the constructor, depending on ow the event handler register works.
1827 + if (event.trust_session.new_state == mir_trust_session_state_stopped)
1828 + delete this;

~~~~~~
2257 + // todo - surfaces should register with the event handler register.
TODO

~~~~
I'm not really sure how the boost containers work... but this doesn't seem right:
3226 + Session* session_fun() const { return session.lock().get(); }

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> 2131 + if (trust_session->get_state() == mir_trust_session_state_stopped)
> 2132 + mir_wait_for(trust_session->stop(null_callback, nullptr));
>
> This doesn't look right (if trust session is stopped, then stop it?)

It isn't right.

But if we had "!=" then we'd hit another problem:

1. The server generates a "mir_event_type_trust_session_state_change" event to mir_trust_session_state_stopped
1.1 the handler registered in the constructor deletes the MirTrustSession.

2. This typically happens before the call to the server receives its completion message - which is then routed to just deleted MirTrustSession which causes "pure virtual method called".

3. If we got past that then we'd delete the MirTrustSession again in mir_trust_session_release_sync()

Basically, this whole stop/release area of code needs fixing.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > 2131 + if (trust_session->get_state() == mir_trust_session_state_stopped)
> > 2132 + mir_wait_for(trust_session->stop(null_callback, nullptr));
> >
> > This doesn't look right (if trust session is stopped, then stop it?)
>
> It isn't right.
>
> But if we had "!=" then we'd hit another problem:
>
> 1. The server generates a "mir_event_type_trust_session_state_change" event to
> mir_trust_session_state_stopped
> 1.1 the handler registered in the constructor deletes the MirTrustSession.
>
> 2. This typically happens before the call to the server receives its
> completion message - which is then routed to just deleted MirTrustSession
> which causes "pure virtual method called".
>
> 3. If we got past that then we'd delete the MirTrustSession again in
> mir_trust_session_release_sync()
>
> Basically, this whole stop/release area of code needs fixing.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Seems strange that we could call 'delete this' in the constructor, depending
> on ow the event handler register works.
> 1827 + if (event.trust_session.new_state ==
> mir_trust_session_state_stopped)
> 1828 + delete this;

It is in a lambda created by the constructor - which might be right if it didn't interact so badly with other code.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> ~~~~~
> 1771 + std::atomic<int> next_fn_id;
> 1772 + std::unordered_set<int> delete_later_ids;
> 1773 + std::atomic<bool> in_event;
>
> do these have to be atomic? seems they could just be protected by the mutex.

They not only could, they already are. On our target architectures the generated code should be identical, so this is just needless verbosity.

I'll push an update.

> Also would be better to do without the recursive_mutex...

I told Nick this before he pushed it. ;)

The problem is that the "stopped" event handler can delete the MirTrustSession - which then unregisters the event handler. (As this is the one handler we can be sure that won't be encountered again during the iteration I'm not sure of the benefits of delete_later_ids vs "thou shalt not unregister other event handlers when processing events".)

*Needs Discussion* on this last point

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > Also would be better to do without the recursive_mutex...
...
> *Needs Discussion* on this last point

Nevermind, found a simple solution

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > > Also would be better to do without the recursive_mutex...
> ...
> > *Needs Discussion* on this last point
>
> Nevermind, found a simple solution

Oh rats! That doesn't work

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > ~~~~~
> > 1771 + std::atomic<int> next_fn_id;
> > 1772 + std::unordered_set<int> delete_later_ids;
> > 1773 + std::atomic<bool> in_event;
> >
> > do these have to be atomic? seems they could just be protected by the mutex.
>
> They not only could, they already are. On our target architectures the
> generated code should be identical, so this is just needless verbosity.
>
> I'll push an update.
>
> > Also would be better to do without the recursive_mutex...
>
> I told Nick this before he pushed it. ;)
>
> The problem is that the "stopped" event handler can delete the MirTrustSession
> - which then unregisters the event handler. (As this is the one handler we can
> be sure that won't be encountered again during the iteration I'm not sure of
> the benefits of delete_later_ids vs "thou shalt not unregister other event
> handlers when processing events".)
>
> *Needs Discussion* on this last point

The tricky part is that with a single lock there can be an unregister() call after the "unlock()" in handle_event(). But after unregister() returns the client justifiably expects there to be no further callbacks.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :
Download full text (4.3 KiB)

(Finally I was able to catch back up on this. It's a bit long, so please bear with me.)

First and foremost, it's important for us (the Mir team) to minimize the impact of ABI/API breaks for obvious reasons. So we shall try to get it as close to right as possible the first time. I'm sure there will be inevitable iterations, but we need to make a conscious effort to minimize breaks. From that perspective, the focus is still on defining an API that captures the minimum requirements for the primary trusted sessions use-case, while leaving open the possibility to a more generalized usage.

That said, here are my thoughts in no particular order :

- The phrase "trusted session" probably makes sense when considering the primary use-case where the normal flow of an application is interrupted with a prompt to get a user to confirm her intent. In Mir, being lower on the stack and unaware of the use-case details, I am not convinced "trusted" is the best choice for this name, as there may be (non-security related use-) cases where surfaces originating from different connections would be considered belonging to the same "logical" mir connection for focus and LC purposes. Perhaps, SuperSession, SessionClique, SessionGroup, etc might work better. (I'll continue calling it "trusted session" until we converge on a name.)

- In the primary use-case mentioned above, the TPP's surface(s) would have to be always on top to prevent the app from proceeding without user's confirmation. In general, should the order of surfaces in aggregate and focus policy be selectable? For example, we could have an enum (say, ALWAYS_ON_TOP) passed into mir_connection_start_trust_session_sync for now. Later, we could add more when the need arises.

- For the primary use-case, it makes sense for a third process (in this case, the trusted helper) to request a trusted session be created on behalf of another process. From Mir's perspective, in a more general sense, the process making the request may at times be starting a trusted session for its own use. Api-wise, the pid passed into mir_connection_start_trust_session_sync would be the pid of the process making the call. As in the examples/trusted_session.c, this should work, so no problem there.

- In general, how does Mir stop malicious applications from pretending to be trusted helpers and trying to establish a "trusted" session with just any process?

- mir_trust_session_new_fds_for_prompt_providers : I'm kind of wondering why this was invented in the first place. In general, I don't like having two different ways of adding processes to the session - by pid, and by having a third party pass FDs. It seems superfluous at best. At worst, there could be security issues (for instance we'd need to make sure that a process already belonging to a trust session isn't handed an FD for the same trust session, or worse, that it isn't trying to create another trust session elsewhere). I'm not saying these use-cases are invalid, but having two different methods could be problematic in terms of bookkeeping.

- Assuming the FD method is kept, we should call it something like : mir_trust_session_allocate_new_connections, dropping the reference that ...

Read more...

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :
Download full text (5.9 KiB)

> (Finally I was able to catch back up on this. It's a bit long, so please bear
> with me.)
>
> First and foremost, it's important for us (the Mir team) to minimize the
> impact of ABI/API breaks for obvious reasons. So we shall try to get it as
> close to right as possible the first time. I'm sure there will be inevitable
> iterations, but we need to make a conscious effort to minimize breaks. From
> that perspective, the focus is still on defining an API that captures the
> minimum requirements for the primary trusted sessions use-case, while leaving
> open the possibility to a more generalized usage.
>
> That said, here are my thoughts in no particular order :
>
> - The phrase "trusted session" probably makes sense when considering the
> primary use-case where the normal flow of an application is interrupted with a
> prompt to get a user to confirm her intent. In Mir, being lower on the stack
> and unaware of the use-case details, I am not convinced "trusted" is the best
> choice for this name, as there may be (non-security related use-) cases where
> surfaces originating from different connections would be considered belonging
> to the same "logical" mir connection for focus and LC purposes. Perhaps,
> SuperSession, SessionClique, SessionGroup, etc might work better. (I'll
> continue calling it "trusted session" until we converge on a name.)

Once you've been reading the code a bit it becomes clear that there is a fine distinction between "trust session" and "trusted session".

A "trust session" is what https://wiki.ubuntu.com/Security/TrustStoreAndSessions refers to as a "Trusted Prompt Session" while a "trusted session" could be either an "application" or a "trust prompt provider". (And yes, this is unnecessarily confusing.)

This usage isn't consistent: some of the code uses "participant" for "application or trust prompt provider"; and, this branch name should be "trust_sessions" not "trusted _sessions".

> - In the primary use-case mentioned above, the TPP's surface(s) would have to
> be always on top to prevent the app from proceeding without user's
> confirmation. In general, should the order of surfaces in aggregate and focus
> policy be selectable? For example, we could have an enum (say, ALWAYS_ON_TOP)
> passed into mir_connection_start_trust_session_sync for now. Later, we could
> add more when the need arises.
>
> - For the primary use-case, it makes sense for a third process (in this case,
> the trusted helper) to request a trusted session be created on behalf of
> another process. From Mir's perspective, in a more general sense, the process
> making the request may at times be starting a trusted session for its own use.
> Api-wise, the pid passed into mir_connection_start_trust_session_sync would be
> the pid of the process making the call. As in the examples/trusted_session.c,
> this should work, so no problem there.
>
> - In general, how does Mir stop malicious applications from pretending to be
> trusted helpers and trying to establish a "trusted" session with just any
> process?
>
> - mir_trust_session_new_fds_for_prompt_providers : I'm kind of wondering why
> this was invented in the first place. In general, I don'...

Read more...

Revision history for this message
Thomas Voß (thomas-voss) :
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

The new names make things a lot clearer.

~~~~

410 +/**
411 + * Return the state of prompt session
412 + * \param [in] prompt_session The prompt session
413 + * \return The state of the prompt session
414 + */
415 +MirPromptSessionState mir_prompt_session_get_state(MirPromptSession *prompt_session);

Is there a need for this function? The client registers an event handler to process state changes, why would it need this as well?

~~~~

815 + virtual std::shared_ptr<Session> application_for_prompt_session(std::shared_ptr<PromptSession> const& prompt_session) const = 0;
816 +
817 + virtual std::shared_ptr<Session> helper_for_prompt_session(std::shared_ptr<PromptSession> const& prompt_session) const = 0;
818 +
819 + virtual void for_each_provider_in_prompt_session(std::shared_ptr<PromptSession> const& prompt_session,
820 + std::function<void(std::shared_ptr<Session> const& prompt_provider)> const& f) const = 0;
821 +

The names for these functions are verbose - I don't think they need the "_prompt_session" suffix as the argument supplied ought to make the meaning clear.

Also they are not used in the acceptance tests - which should be delineating the functionality Mir needs to provide. I.e. either there are missing tests or these functions are not needed.

~~~~

370 +MirPromptSession *mir_connection_start_prompt_session_sync(MirConnection* connection,
371 + pid_t application_pid,
372 + mir_prompt_session_event_callback event_callback,
373 + void *context);

Better as "create" to match the "release"

review: Needs Fixing
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Still reviewing... I still see "trusted" in a few places :

87 +///\page prompt_session.c prompt_session.c: A mir client which starts a prompt session and trusted client app.
126 + printf("Trust Session state updated to %d\n", state);
192 + printf("helper: added trusted session pid: %d\n", child_pid);
206 + puts("helper: Trusted session stoped by server");
279 + // This simulates the helper starting a new application which it adds to the trusted session.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Still reviewing... I still see "trusted" in a few places :
>
> 87 +///\page prompt_session.c prompt_session.c: A mir client which starts
> a prompt session and trusted client app.
> 126 + printf("Trust Session state updated to %d\n", state);
> 192 + printf("helper: added trusted session pid: %d\n", child_pid);
> 206 + puts("helper: Trusted session stoped by server");
> 279 + // This simulates the helper starting a new application which it
> adds to the trusted session.

Seems to be just the example .c file. Note also "s/stoped/stopped/

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

70 +#define _POSIX_SOURCE

Not needed

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

A bunch of comments inlined. After going through it, I have yet to understand what this has to do with Mir. Mir definitely can support it but this functionality doesn't really interact much with other parts of Mir, or conversely, (other parts of) Mir doesn't really depend on it. Hopefully I'm missing something.

review: Needs Fixing
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) :
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> 70 +#define _POSIX_SOURCE
>
> Not needed

kill() seems to require this.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I hate diff comments in their current form. Too much scrolling to find them.

review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

Commented on one's that need commenting on. Fixed others.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

478 +#include "mir_toolkit/common.h"
479 +
480 +#include <sys/types.h>
481 +#include <vector>
482 +#include <string>
483 +#include <memory>

Not needed.

1307 + return std::shared_ptr<scene::PromptSession>();

Indentation.

1895 + std::unique_lock<decltype(mutex)> lock(mutex);
1904 + std::unique_lock<decltype(mutex)> lock(mutex);
1911 + std::unique_lock<decltype(mutex)> lock(mutex);

No need for (the more expensive) std::unique_lock<>. std::lock_guard<> is sufficient.

1913 + auto const event_handlers_copy(event_handlers);
1917 + // Ensure handler wasn't unregistered since making copy
1918 + if (event_handlers.find(handler.first) != event_handlers.end())

With the current state of the code (handler called under lock) we don't have to worry about "event_handlers" changing beneath our feet, so neither the copy nor the unregistration check is needed. I presume that the pros and cons of calling the handler under lock have been considered and we have explicitly decided to go with the current approach (I have probably missed some of the discussion around this).

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> 1913 + auto const event_handlers_copy(event_handlers);
> 1917 + // Ensure handler wasn't unregistered since making copy
> 1918 + if (event_handlers.find(handler.first) != event_handlers.end())
>
> With the current state of the code (handler called under lock) we don't have
> to worry about "event_handlers" changing beneath our feet, so neither the copy
> nor the unregistration check is needed. I presume that the pros and cons of
> calling the handler under lock have been considered and we have explicitly
> decided to go with the current approach (I have probably missed some of the
> discussion around this).

It is a *recursive* mutex so a handler can deregister itself - but it *could* make other changes.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> It is a *recursive* mutex so a handler can deregister itself - but it *could* make other changes.

Right, I forgot that we had a recursive mutex (and using decltype didn't help reminding me).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I hate diff comments in their current form. Too much scrolling to find them.

The "Needs fixing" was accidental

review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

LGTM

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

> I'm not really sure how the boost containers work... but this doesn't seem
> right:
> 3226 + Session* session_fun() const { return session.lock().get(); }

It doesn't seem right to me either.

Using the referent of a weak pointer as a key is going to lead to undefined behavior when the key changes. (I'm not sure why the weak pointer isn't used directly which would avoid problems.)

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > I'm not really sure how the boost containers work... but this doesn't seem
> > right:
> > 3226 + Session* session_fun() const { return session.lock().get(); }
>
> It doesn't seem right to me either.
>
> Using the referent of a weak pointer as a key is going to lead to undefined
> behavior when the key changes. (I'm not sure why the weak pointer isn't used
> directly which would avoid problems.)

Oops! I thought I'd voted for fixing.

review: Needs Fixing
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

I am OK with this as a first take on prompt/trust/? sessions. The size of the MP means that it is likely that we missed something, but we can keep refactoring as the requirements crystallize.

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

> > > I'm not really sure how the boost containers work... but this doesn't seem
> > > right:
> > > 3226 + Session* session_fun() const { return session.lock().get(); }
> >
> > It doesn't seem right to me either.
> >
> > Using the referent of a weak pointer as a key is going to lead to undefined
> > behavior when the key changes. (I'm not sure why the weak pointer isn't used
> > directly which would avoid problems.)
>
> Oops! I thought I'd voted for fixing.

And fixed

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Hmm! There should be acceptance tests that use fds for prompt providers.

(I won't block as I don't mind adding those in a followup MP.)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
kevin gunn (kgunn72) wrote :

re-top-approving...jenkins testing succeeds, but not merging

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/CMakeLists.txt'
2--- examples/CMakeLists.txt 2014-06-02 17:07:02 +0000
3+++ examples/CMakeLists.txt 2014-06-16 15:01:17 +0000
4@@ -92,6 +92,9 @@
5
6 )
7
8+add_executable(mir_demo_client_prompt_session prompt_session.c)
9+target_link_libraries(mir_demo_client_prompt_session mirclient)
10+
11 add_library(mirdraw STATIC graphics_utils.cpp)
12 target_link_libraries(mirdraw ${GLESv2_LIBRARIES})
13
14@@ -143,7 +146,7 @@
15 server_configuration.cpp
16 )
17
18-target_link_libraries(mir_demo_standalone_input_filter
19+target_link_libraries(mir_demo_standalone_input_filter
20 mirserver
21 )
22
23@@ -158,8 +161,9 @@
24 mir_demo_client_multiwin
25 mir_demo_client_display_config
26 mir_demo_client_progressbar
27+ mir_demo_client_prompt_session
28 mir_demo_standalone_input_filter
29- mir_demo_standalone_render_to_fb
30+ mir_demo_standalone_render_to_fb
31 mir_demo_standalone_render_surfaces
32 )
33
34
35=== modified file 'examples/demo-inprocess-surface-client/inprocess_egl_client.cpp'
36--- examples/demo-inprocess-surface-client/inprocess_egl_client.cpp 2014-04-15 05:31:19 +0000
37+++ examples/demo-inprocess-surface-client/inprocess_egl_client.cpp 2014-06-16 15:01:17 +0000
38@@ -102,7 +102,7 @@
39
40 auto input_platform = mircv::InputPlatform::create();
41 input_thread = input_platform->create_input_thread(
42- surface->client_input_fd(),
43+ surface->client_input_fd(),
44 std::bind(std::mem_fn(&me::InprocessEGLClient::handle_event), this, std::placeholders::_1));
45 input_thread->start();
46
47
48=== added file 'examples/prompt_session.c'
49--- examples/prompt_session.c 1970-01-01 00:00:00 +0000
50+++ examples/prompt_session.c 2014-06-16 15:01:17 +0000
51@@ -0,0 +1,231 @@
52+/*
53+ * Copyright © 2014 Canonical Ltd.
54+ *
55+ * This program is free software: you can redistribute it and/or modify
56+ * it under the terms of the GNU General Public License version 3 as
57+ * published by the Free Software Foundation.
58+ *
59+ * This program is distributed in the hope that it will be useful,
60+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
61+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62+ * GNU General Public License for more details.
63+ *
64+ * You should have received a copy of the GNU General Public License
65+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
66+ *
67+ * Authored by: Nick Dedekind <nick.dedekind <nick.dedekind@canonical.com>
68+ */
69+
70+#define _POSIX_SOURCE
71+
72+#include "mir_toolkit/mir_client_library.h"
73+#include "mir_toolkit/mir_prompt_session.h"
74+
75+#undef NDEBUG
76+#include <assert.h>
77+#include <string.h>
78+#include <stdio.h>
79+#include <stdlib.h>
80+#include <getopt.h>
81+#include <unistd.h>
82+#include <errno.h>
83+#include <sys/wait.h>
84+#include <sys/types.h>
85+#include <signal.h>
86+
87+///\page prompt_session.c prompt_session.c: A mir client which starts a prompt session and prompt client app.
88+/// mir_demo_client_prompt_session shows the use of mir prompt session API.
89+/// This program opens a mir connection and creates a prompt session.
90+///\section helper helper()
91+/// Opens a mir connection and creates a prompt session
92+/// before closing the prompt session and connection.
93+///\section prompt_session_app prompt_session_app()
94+/// Opens a mir connection and creates a surface
95+/// before releasing the surface and closing the connection.
96+///\example prompt_session.c A mir client demonstrating prompt sessions.
97+///\section MirDemoState MirDemoState
98+/// The handles needs to be accessible both to callbacks and to the control function.
99+///\snippet prompt_session.c MirDemoState_tag
100+///\section Callbacks Callbacks
101+///\snippet prompt_session.c Callback_tag
102+/// This program creates two processes, both opening a mir connection, one starting
103+/// a prompt session with the other process.
104+
105+///\internal [MirDemoState_tag]
106+// Utility structure for the state of a single session.
107+typedef struct MirDemoState
108+{
109+ MirConnection *connection;
110+ MirSurface *surface;
111+ MirPromptSession *prompt_session;
112+ pid_t child_pid;
113+ MirPromptSessionState state;
114+
115+ int* client_fds;
116+ unsigned int client_fd_count;
117+} MirDemoState;
118+///\internal [MirDemoState_tag]
119+
120+
121+///\internal [Callback_tag]
122+// Callback to update MirDemoState on prompt_session_event
123+static void prompt_session_event_callback(MirPromptSession* prompt_session,
124+ MirPromptSessionState state,
125+ void* context)
126+{
127+ (void)prompt_session;
128+ MirDemoState* demo_state = (MirDemoState*)context;
129+ demo_state->state = state;
130+
131+ printf("helper: Prompt Session state updated to %d\n", state);
132+ if (state == mir_prompt_session_state_stopped)
133+ {
134+ kill(demo_state->child_pid, SIGINT);
135+ }
136+}
137+
138+static void client_fd_callback(MirPromptSession* prompt_session, size_t count, int const* fds, void* context)
139+{
140+ (void)prompt_session;
141+ ((MirDemoState*)context)->client_fds = malloc(sizeof(int)*count);
142+ unsigned int i = 0;
143+ for (; i < count; i++)
144+ {
145+ ((MirDemoState*)context)->client_fds[i] = fds[i];
146+ }
147+ ((MirDemoState*)context)->client_fd_count = count;
148+}
149+///\internal [Callback_tag]
150+
151+void start_session(const char* server, const char* name, MirDemoState* mcd)
152+{
153+ // Call mir_connect synchronously
154+ mcd->connection = mir_connect_sync(server, name);
155+
156+ // We expect a connection handle;
157+ // we expect it to be valid; and,
158+ // we don't expect an error description
159+ assert(mcd->connection != NULL);
160+ assert(mir_connection_is_valid(mcd->connection));
161+ assert(strcmp(mir_connection_get_error_message(mcd->connection), "") == 0);
162+ printf("%s: Connected\n", name);
163+
164+ // We can query information about the platform we're running on
165+ {
166+ MirPlatformPackage platform_package;
167+ platform_package.data_items = -1;
168+ platform_package.fd_items = -1;
169+
170+ mir_connection_get_platform(mcd->connection, &platform_package);
171+ assert(0 <= platform_package.data_items);
172+ assert(0 <= platform_package.fd_items);
173+ }
174+}
175+
176+void stop_session(MirDemoState* mcd, const char* name)
177+{
178+ if (mcd->surface)
179+ {
180+ // We should release our surface
181+ mir_surface_release_sync(mcd->surface);
182+ mcd->surface = 0;
183+ printf("%s: Surface released\n", name);
184+ }
185+
186+ // We should release our connection
187+ mir_connection_release(mcd->connection);
188+ printf("%s: Connection released\n", name);
189+}
190+
191+void helper(const char* server)
192+{
193+ MirDemoState mcd;
194+ mcd.connection = 0;
195+ mcd.surface = 0;
196+ mcd.prompt_session = 0;
197+ mcd.state = mir_prompt_session_state_stopped;
198+ mcd.client_fd_count = 0;
199+ start_session(server, "helper", &mcd);
200+
201+ // We create a prompt session
202+ mcd.prompt_session = mir_connection_create_prompt_session_sync(mcd.connection, getpid(), prompt_session_event_callback, &mcd);
203+ assert(mcd.prompt_session != NULL);
204+
205+ assert(mcd.state == mir_prompt_session_state_started);
206+ puts("helper: Started prompt session");
207+
208+ mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(mcd.prompt_session, 1, client_fd_callback, &mcd));
209+ assert(mcd.client_fd_count == 1);
210+ puts("helper: Added waiting FD");
211+
212+ printf("helper: Starting child application 'mir_demo_client_basic' with fd://%d\n", mcd.client_fds[0]);
213+ mcd.child_pid = fork();
214+
215+ if (mcd.child_pid == 0)
216+ {
217+ char buffer[128] = {0};
218+ sprintf(buffer, "fd://%d", mcd.client_fds[0]);
219+
220+ char* args[4];
221+ args[0] = "mir_demo_client_basic";
222+ args[1] = "-m";
223+ args[2] = &buffer[0];
224+ args[3] = NULL;
225+
226+ errno = 0;
227+ execvp("mir_demo_client_basic", args);
228+ return;
229+ }
230+
231+ int status;
232+ printf("helper: Waiting on child application: %d\n", mcd.child_pid);
233+ waitpid(mcd.child_pid, &status, 0);
234+
235+ if (mcd.state == mir_prompt_session_state_started)
236+ {
237+ mir_prompt_session_release_sync(mcd.prompt_session);
238+ mcd.prompt_session = NULL;
239+ puts("helper: Stopped prompt session");
240+ }
241+ else
242+ {
243+ puts("helper: Prompt session stopped by server");
244+ }
245+ puts("helper: Done");
246+
247+ stop_session(&mcd, "helper");
248+}
249+
250+// The main() function deals with parsing arguments and defaults
251+int main(int argc, char* argv[])
252+{
253+ // Some variables for holding command line options
254+ char const *server = NULL;
255+
256+ // Parse the command line
257+ {
258+ int arg;
259+ opterr = 0;
260+ while ((arg = getopt (argc, argv, "c:hm:")) != -1)
261+ {
262+ switch (arg)
263+ {
264+ case 'm':
265+ server = optarg;
266+ break;
267+
268+ case '?':
269+ case 'h':
270+ default:
271+ puts(argv[0]);
272+ puts("Usage:");
273+ puts(" -m <Mir server socket>");
274+ puts(" -h: this help text");
275+ return -1;
276+ }
277+ }
278+ }
279+
280+ helper(server);
281+ return 0;
282+}
283
284=== modified file 'include/client/mir_toolkit/mir_connection.h'
285--- include/client/mir_toolkit/mir_connection.h 2014-06-02 17:07:02 +0000
286+++ include/client/mir_toolkit/mir_connection.h 2014-06-16 15:01:17 +0000
287@@ -169,26 +169,6 @@
288 MirConnection* connection, MirPixelFormat* formats,
289 unsigned const int format_size, unsigned int *num_valid_formats);
290
291-/**
292- * Allocate some FDs for trusted clients to connect on
293- *
294- * Trust session helpers need to allocate connection FDs it will pass to
295- * trusted clients to use when connecting to the server. The server can
296- * then associate them with the trust session.
297- *
298- * \warning This API is tentative until the implementation of trust sessions is complete
299- * \param [in] connection The connection
300- * \param [in] no_of_fds The number of fds to allocate
301- * \param [in] callback Callback invoked when request completes
302- * \param [in,out] context User data passed to the callback function
303- * \return A handle that can be passed to mir_wait_for
304- */
305-MirWaitHandle* mir_connection_new_fds_for_trusted_clients(
306- MirConnection* connection,
307- unsigned int no_of_fds,
308- mir_client_fd_callback callback,
309- void * context);
310-
311 #ifdef __cplusplus
312 }
313 /**@}*/
314
315=== added file 'include/client/mir_toolkit/mir_prompt_session.h'
316--- include/client/mir_toolkit/mir_prompt_session.h 1970-01-01 00:00:00 +0000
317+++ include/client/mir_toolkit/mir_prompt_session.h 2014-06-16 15:01:17 +0000
318@@ -0,0 +1,87 @@
319+/*
320+ * Copyright © 2014 Canonical Ltd.
321+ *
322+ * This program is free software: you can redistribute it and/or modify it
323+ * under the terms of the GNU Lesser General Public License version 3,
324+ * as published by the Free Software Foundation.
325+ *
326+ * This program is distributed in the hope that it will be useful,
327+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
328+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
329+ * GNU Lesser General Public License for more details.
330+ *
331+ * You should have received a copy of the GNU Lesser General Public License
332+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
333+ */
334+
335+#ifndef MIR_TOOLKIT_MIR_PROMPT_SESSION_H_
336+#define MIR_TOOLKIT_MIR_PROMPT_SESSION_H_
337+
338+#include "mir_toolkit/mir_client_library.h"
339+
340+#include <sys/types.h>
341+
342+#ifdef __cplusplus
343+/**
344+ * \addtogroup mir_toolkit
345+ * @{
346+ */
347+extern "C" {
348+#endif
349+
350+/**
351+ * Create and start a new prompt session
352+ * \param [in] connection The connection
353+ * \param [in] application_pid The process id of the initiating application
354+ * \param [in] state_change_callback The function to be called when a prompt session state change occurs
355+ * \param [in,out] context User data passed to the callback functions
356+ * \return A handle that can be passed to mir_wait_for
357+ */
358+MirPromptSession *mir_connection_create_prompt_session_sync(
359+ MirConnection* connection,
360+ pid_t application_pid,
361+ mir_prompt_session_state_change_callback state_change_callback,
362+ void *context);
363+
364+/**
365+ * Add a prompt provider process id to the prompt session
366+ * \param [in] prompt_session The prompt session
367+ * \param [in] provider_pid The process id of the prompt provider to add
368+ * \return True if the process id was added, false otherwise
369+ */
370+MirBool mir_prompt_session_add_prompt_provider_sync(
371+ MirPromptSession *prompt_session,
372+ pid_t provider_pid);
373+
374+/**
375+ * Allocate some FDs for prompt providers to connect on
376+ *
377+ * Prompt helpers need to allocate connection FDs it will pass to
378+ * prompt providers to use when connecting to the server. The server can
379+ * then associate them with the prompt session.
380+ *
381+ * \warning This API is tentative until the implementation of prompt sessions is complete
382+ * \param [in] prompt_session The prompt session
383+ * \param [in] no_of_fds The number of fds to allocate
384+ * \param [in] callback Callback invoked when request completes
385+ * \param [in,out] context User data passed to the callback function
386+ * \return A handle that can be passed to mir_wait_for
387+ */
388+MirWaitHandle* mir_prompt_session_new_fds_for_prompt_providers(
389+ MirPromptSession *prompt_session,
390+ unsigned int no_of_fds,
391+ mir_client_fd_callback callback,
392+ void * context);
393+
394+/**
395+ * Stop and release the specified prompt session
396+ * \param [in] prompt_session The prompt session
397+ */
398+void mir_prompt_session_release_sync(MirPromptSession *prompt_session);
399+
400+#ifdef __cplusplus
401+}
402+/**@}*/
403+#endif
404+
405+#endif /* MIR_TOOLKIT_MIR_PROMPT_SESSION_H_ */
406
407=== modified file 'include/server/mir/default_server_configuration.h'
408--- include/server/mir/default_server_configuration.h 2014-06-09 17:16:32 +0000
409+++ include/server/mir/default_server_configuration.h 2014-06-16 15:01:17 +0000
410@@ -93,6 +93,8 @@
411 class SurfaceStackModel;
412 class SurfaceStack;
413 class SceneReport;
414+class PromptSessionListener;
415+class PromptSessionManager;
416 }
417 namespace graphics
418 {
419@@ -231,6 +233,8 @@
420 virtual std::shared_ptr<scene::PlacementStrategy> the_placement_strategy();
421 virtual std::shared_ptr<scene::SessionListener> the_session_listener();
422 virtual std::shared_ptr<shell::DisplayLayout> the_shell_display_layout();
423+ virtual std::shared_ptr<scene::PromptSessionListener> the_prompt_session_listener();
424+ virtual std::shared_ptr<scene::PromptSessionManager> the_prompt_session_manager();
425 /** @} */
426
427 /** @name internal scene configuration
428@@ -365,6 +369,8 @@
429 CachedPtr<scene::MediatingDisplayChanger> mediating_display_changer;
430 CachedPtr<graphics::GLProgramFactory> gl_program_factory;
431 CachedPtr<graphics::GLConfig> gl_config;
432+ CachedPtr<scene::PromptSessionListener> prompt_session_listener;
433+ CachedPtr<scene::PromptSessionManager> prompt_session_manager;
434 CachedPtr<scene::SessionCoordinator> session_coordinator;
435 CachedPtr<EmergencyCleanup> emergency_cleanup;
436
437
438=== added file 'include/server/mir/frontend/prompt_session.h'
439--- include/server/mir/frontend/prompt_session.h 1970-01-01 00:00:00 +0000
440+++ include/server/mir/frontend/prompt_session.h 2014-06-16 15:01:17 +0000
441@@ -0,0 +1,48 @@
442+/*
443+ * Copyright © 2014 Canonical Ltd.
444+ *
445+ * This program is free software: you can redistribute it and/or modify it
446+ * under the terms of the GNU General Public License version 3,
447+ * as published by the Free Software Foundation.
448+ *
449+ * This program is distributed in the hope that it will be useful,
450+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
451+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
452+ * GNU General Public License for more details.
453+ *
454+ * You should have received a copy of the GNU General Public License
455+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
456+ *
457+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
458+ */
459+
460+#ifndef MIR_FRONTEND_PROMPT_SESSION_H_
461+#define MIR_FRONTEND_PROMPT_SESSION_H_
462+
463+#include "mir_toolkit/common.h"
464+
465+#include <sys/types.h>
466+#include <vector>
467+#include <string>
468+#include <memory>
469+
470+namespace mir
471+{
472+
473+namespace frontend
474+{
475+class PromptSession
476+{
477+public:
478+ virtual ~PromptSession() = default;
479+
480+protected:
481+ PromptSession() = default;
482+ PromptSession(const PromptSession&) = delete;
483+ PromptSession& operator=(const PromptSession&) = delete;
484+};
485+
486+}
487+}
488+
489+#endif // MIR_FRONTEND_PROMPT_SESSION_H_
490
491=== modified file 'include/server/mir/frontend/session_mediator_report.h'
492--- include/server/mir/frontend/session_mediator_report.h 2014-05-12 21:11:05 +0000
493+++ include/server/mir/frontend/session_mediator_report.h 2014-06-16 15:01:17 +0000
494@@ -1,5 +1,5 @@
495 /*
496- * Copyright © 2012 Canonical Ltd.
497+ * Copyright © 2012-2014 Canonical Ltd.
498 *
499 * This program is free software: you can redistribute it and/or modify it
500 * under the terms of the GNU General Public License version 3,
501@@ -22,6 +22,8 @@
502
503 #include <string>
504
505+#include <sys/types.h>
506+
507 namespace mir
508 {
509 namespace frontend
510@@ -50,6 +52,12 @@
511
512 virtual void session_configure_display_called(std::string const& app_name) = 0;
513
514+ virtual void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) = 0;
515+
516+ virtual void session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process) = 0;
517+
518+ virtual void session_stop_prompt_session_called(std::string const& app_name) = 0;
519+
520 virtual void session_error(
521 std::string const& app_name,
522 char const* method,
523
524=== modified file 'include/server/mir/frontend/shell.h'
525--- include/server/mir/frontend/shell.h 2014-04-15 05:31:19 +0000
526+++ include/server/mir/frontend/shell.h 2014-06-16 15:01:17 +0000
527@@ -30,11 +30,13 @@
528 namespace scene
529 {
530 struct SurfaceCreationParameters;
531+struct PromptSessionCreationParameters;
532 }
533 namespace frontend
534 {
535 class EventSink;
536 class Session;
537+class PromptSession;
538
539 class Shell
540 {
541@@ -54,6 +56,14 @@
542
543 virtual void handle_surface_created(std::shared_ptr<Session> const& session) = 0;
544
545+ virtual std::shared_ptr<PromptSession> start_prompt_session_for(std::shared_ptr<Session> const& session,
546+ scene::PromptSessionCreationParameters const& params) = 0;
547+ virtual void add_prompt_provider_process_for(std::shared_ptr<PromptSession> const& prompt_session,
548+ pid_t process_id) = 0;
549+ virtual void add_prompt_provider_for(std::shared_ptr<PromptSession> const& prompt_session,
550+ std::shared_ptr<Session> const& session) = 0;
551+ virtual void stop_prompt_session(std::shared_ptr<PromptSession> const& prompt_session) = 0;
552+
553 protected:
554 Shell() = default;
555 Shell(const Shell&) = delete;
556
557=== added file 'include/server/mir/scene/null_prompt_session_listener.h'
558--- include/server/mir/scene/null_prompt_session_listener.h 1970-01-01 00:00:00 +0000
559+++ include/server/mir/scene/null_prompt_session_listener.h 2014-06-16 15:01:17 +0000
560@@ -0,0 +1,40 @@
561+/*
562+ * Copyright © 2014 Canonical Ltd.
563+ *
564+ * This program is free software: you can redistribute it and/or modify it
565+ * under the terms of the GNU General Public License version 3,
566+ * as published by the Free Software Foundation.
567+ *
568+ * This program is distributed in the hope that it will be useful,
569+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
570+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
571+ * GNU General Public License for more details.
572+ *
573+ * You should have received a copy of the GNU General Public License
574+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
575+ *
576+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
577+ */
578+
579+#ifndef MIR_SCENE_NULL_PROMPT_SESSION_LISTENER_H_
580+#define MIR_SCENE_NULL_PROMPT_SESSION_LISTENER_H_
581+
582+#include "mir/scene/prompt_session_listener.h"
583+
584+namespace mir
585+{
586+namespace scene
587+{
588+class NullPromptSessionListener : public PromptSessionListener
589+{
590+public:
591+ void starting(std::shared_ptr<PromptSession> const&) override {}
592+ void stopping(std::shared_ptr<PromptSession> const&) override {}
593+
594+ void prompt_provider_added(PromptSession const&, std::shared_ptr<Session> const&) override {}
595+ void prompt_provider_removed(PromptSession const&, std::shared_ptr<Session> const&) override {}
596+};
597+}
598+}
599+
600+#endif // MIR_SHELL_NULL_PROMPT_SESSION_LISTENER_H_
601
602=== added file 'include/server/mir/scene/prompt_session.h'
603--- include/server/mir/scene/prompt_session.h 1970-01-01 00:00:00 +0000
604+++ include/server/mir/scene/prompt_session.h 2014-06-16 15:01:17 +0000
605@@ -0,0 +1,37 @@
606+/*
607+ * Copyright © 2014 Canonical Ltd.
608+ *
609+ * This program is free software: you can redistribute it and/or modify it
610+ * under the terms of the GNU General Public License version 3,
611+ * as published by the Free Software Foundation.
612+ *
613+ * This program is distributed in the hope that it will be useful,
614+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
615+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
616+ * GNU General Public License for more details.
617+ *
618+ * You should have received a copy of the GNU General Public License
619+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
620+ *
621+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
622+ */
623+
624+#ifndef MIR_SCENE_PROMPT_SESSION_H_
625+#define MIR_SCENE_PROMPT_SESSION_H_
626+
627+#include "mir/frontend/prompt_session.h"
628+
629+namespace mir
630+{
631+namespace scene
632+{
633+class Session;
634+
635+class PromptSession : public frontend::PromptSession
636+{
637+};
638+
639+}
640+}
641+
642+#endif // MIR_SHELL_PROMPT_SESSION_H_
643
644=== added file 'include/server/mir/scene/prompt_session_creation_parameters.h'
645--- include/server/mir/scene/prompt_session_creation_parameters.h 1970-01-01 00:00:00 +0000
646+++ include/server/mir/scene/prompt_session_creation_parameters.h 2014-06-16 15:01:17 +0000
647@@ -0,0 +1,36 @@
648+/*
649+ * Copyright © 2014 Canonical Ltd.
650+ *
651+ * This program is free software: you can redistribute it and/or modify it
652+ * under the terms of the GNU General Public License version 3,
653+ * as published by the Free Software Foundation.
654+ *
655+ * This program is distributed in the hope that it will be useful,
656+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
657+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
658+ * GNU General Public License for more details.
659+ *
660+ * You should have received a copy of the GNU General Public License
661+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
662+ *
663+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
664+ */
665+
666+#ifndef MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_
667+#define MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_
668+
669+#include <sys/types.h>
670+
671+namespace mir
672+{
673+namespace scene
674+{
675+
676+struct PromptSessionCreationParameters
677+{
678+ pid_t application_pid = 0;
679+};
680+}
681+}
682+
683+#endif /* MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_ */
684
685=== added file 'include/server/mir/scene/prompt_session_listener.h'
686--- include/server/mir/scene/prompt_session_listener.h 1970-01-01 00:00:00 +0000
687+++ include/server/mir/scene/prompt_session_listener.h 2014-06-16 15:01:17 +0000
688@@ -0,0 +1,52 @@
689+/*
690+ * Copyright © 2014 Canonical Ltd.
691+ *
692+ * This program is free software: you can redistribute it and/or modify it
693+ * under the terms of the GNU General Public License version 3,
694+ * as published by the Free Software Foundation.
695+ *
696+ * This program is distributed in the hope that it will be useful,
697+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
698+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
699+ * GNU General Public License for more details.
700+ *
701+ * You should have received a copy of the GNU General Public License
702+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
703+ *
704+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
705+ */
706+
707+#ifndef MIR_SCENE_PROMPT_SESSION_LISTENER_H_
708+#define MIR_SCENE_PROMPT_SESSION_LISTENER_H_
709+
710+#include <memory>
711+
712+namespace mir
713+{
714+namespace scene
715+{
716+class Session;
717+class PromptSession;
718+
719+class PromptSessionListener
720+{
721+public:
722+ virtual void starting(std::shared_ptr<PromptSession> const& prompt_session) = 0;
723+ virtual void stopping(std::shared_ptr<PromptSession> const& prompt_session) = 0;
724+
725+ virtual void prompt_provider_added(PromptSession const& prompt_session, std::shared_ptr<Session> const& prompt_provider) = 0;
726+ virtual void prompt_provider_removed(PromptSession const& prompt_session, std::shared_ptr<Session> const& prompt_provider) = 0;
727+
728+protected:
729+ PromptSessionListener() = default;
730+ virtual ~PromptSessionListener() = default;
731+
732+ PromptSessionListener(const PromptSessionListener&) = delete;
733+ PromptSessionListener& operator=(const PromptSessionListener&) = delete;
734+};
735+
736+}
737+}
738+
739+
740+#endif // MIR_SCENE_PROMPT_SESSION_LISTENER_H_
741
742=== added file 'include/server/mir/scene/prompt_session_manager.h'
743--- include/server/mir/scene/prompt_session_manager.h 1970-01-01 00:00:00 +0000
744+++ include/server/mir/scene/prompt_session_manager.h 2014-06-16 15:01:17 +0000
745@@ -0,0 +1,110 @@
746+/*
747+ * Copyright © 2014 Canonical Ltd.
748+ *
749+ * This program is free software: you can redistribute it and/or modify it
750+ * under the terms of the GNU General Public License version 3,
751+ * as published by the Free Software Foundation.
752+ *
753+ * This program is distributed in the hope that it will be useful,
754+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
755+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
756+ * GNU General Public License for more details.
757+ *
758+ * You should have received a copy of the GNU General Public License
759+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
760+ *
761+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
762+ */
763+
764+#ifndef MIR_SCENE_PROMPT_SESSION_MANAGER_H_
765+#define MIR_SCENE_PROMPT_SESSION_MANAGER_H_
766+
767+#include <sys/types.h>
768+#include <memory>
769+#include <functional>
770+
771+namespace mir
772+{
773+namespace scene
774+{
775+class Session;
776+class PromptSession;
777+struct PromptSessionCreationParameters;
778+
779+class PromptSessionManager
780+{
781+public:
782+ virtual ~PromptSessionManager() = default;
783+
784+ /**
785+ * Start a new prompt session
786+ * \param [in] session The prompt helper session
787+ * \param [in] params The creation parameters for constructing the prompt session
788+ */
789+ virtual std::shared_ptr<PromptSession> start_prompt_session_for(std::shared_ptr<Session> const& session,
790+ PromptSessionCreationParameters const& params) const = 0;
791+
792+ /**
793+ * Stop a started prompt session
794+ * \param [in] prompt_session The prompt session
795+ */
796+ virtual void stop_prompt_session(std::shared_ptr<PromptSession> const& prompt_session) const = 0;
797+
798+ /**
799+ * Add a prompt provider to an existing prompt session
800+ * \param [in] prompt_session The prompt session
801+ * \param [in] prompt_provider The prompt provider to add to the prompt session
802+ */
803+ virtual void add_prompt_provider(std::shared_ptr<PromptSession> const& prompt_session,
804+ std::shared_ptr<Session> const& prompt_provider) const = 0;
805+
806+ /**
807+ * Add a process id to wait for to the prompt session
808+ * \param [in] prompt_session The prompt session
809+ * \param [in] process_id The process id to wait for
810+ */
811+ virtual void add_prompt_provider_by_pid(std::shared_ptr<PromptSession> const& prompt_session,
812+ pid_t process_id) const = 0;
813+
814+ /**
815+ * Add a session that may have been expected by a prompt session.
816+ * \param [in] session The new session that was being expected
817+ */
818+ virtual void add_expected_session(std::shared_ptr<Session> const& new_session) const = 0;
819+
820+ /**
821+ * Remove a session from all associated prompt sessions
822+ * \param [in] session The new session that is to be removed
823+ */
824+ virtual void remove_session(std::shared_ptr<Session> const& session) const = 0;
825+
826+ /**
827+ * Retrieve the application session for a prompt session
828+ * \param [in] prompt_session The prompt session
829+ */
830+ virtual std::shared_ptr<Session> application_for(std::shared_ptr<PromptSession> const& prompt_session) const = 0;
831+
832+ /**
833+ * Retrieve the helper session for a prompt session
834+ * \param [in] prompt_session The prompt session
835+ */
836+ virtual std::shared_ptr<Session> helper_for(std::shared_ptr<PromptSession> const& prompt_session) const = 0;
837+
838+ /**
839+ * Iterate over all the prompt providers associated with a prompt session
840+ * \param [in] prompt_session The prompt session
841+ * \param [in] f The callback function to call for each provider
842+ */
843+ virtual void for_each_provider_in(std::shared_ptr<PromptSession> const& prompt_session,
844+ std::function<void(std::shared_ptr<Session> const& prompt_provider)> const& f) const = 0;
845+
846+protected:
847+ PromptSessionManager() = default;
848+ PromptSessionManager(const PromptSessionManager&) = delete;
849+ PromptSessionManager& operator=(const PromptSessionManager&) = delete;
850+};
851+
852+}
853+}
854+
855+#endif // MIR_SCENE_PROMPT_SESSION_MANAGER_H_
856
857=== modified file 'include/server/mir/scene/session.h'
858--- include/server/mir/scene/session.h 2014-04-15 05:31:19 +0000
859+++ include/server/mir/scene/session.h 2014-06-16 15:01:17 +0000
860@@ -40,6 +40,9 @@
861 virtual std::shared_ptr<Surface> default_surface() const = 0;
862 virtual void set_lifecycle_state(MirLifecycleState state) = 0;
863 virtual void send_display_config(graphics::DisplayConfiguration const&) = 0;
864+
865+ virtual void start_prompt_session() = 0;
866+ virtual void stop_prompt_session() = 0;
867 };
868 }
869 }
870
871=== modified file 'include/server/mir/shell/session_coordinator_wrapper.h'
872--- include/server/mir/shell/session_coordinator_wrapper.h 2014-05-23 12:59:00 +0000
873+++ include/server/mir/shell/session_coordinator_wrapper.h 2014-06-16 15:01:17 +0000
874@@ -48,6 +48,20 @@
875
876 void handle_surface_created(std::shared_ptr<frontend::Session> const& session) override;
877
878+ std::shared_ptr<frontend::PromptSession> start_prompt_session_for(
879+ std::shared_ptr<frontend::Session> const& session,
880+ scene::PromptSessionCreationParameters const& params) override;
881+
882+ void add_prompt_provider_process_for(
883+ std::shared_ptr<frontend::PromptSession> const& prompt_session,
884+ pid_t process_id) override;
885+
886+ void add_prompt_provider_for(
887+ std::shared_ptr<frontend::PromptSession> const& prompt_session,
888+ std::shared_ptr<frontend::Session> const& session) override;
889+
890+ void stop_prompt_session(std::shared_ptr<frontend::PromptSession> const& prompt_session) override;
891+
892 protected:
893 std::shared_ptr<scene::SessionCoordinator> const wrapped;
894 };
895
896=== modified file 'include/shared/mir_toolkit/client_types.h'
897--- include/shared/mir_toolkit/client_types.h 2014-06-02 17:07:02 +0000
898+++ include/shared/mir_toolkit/client_types.h 2014-06-16 15:01:17 +0000
899@@ -46,6 +46,7 @@
900 typedef struct MirConnection MirConnection;
901 typedef struct MirSurface MirSurface;
902 typedef struct MirScreencast MirScreencast;
903+typedef struct MirPromptSession MirPromptSession;
904
905 /**
906 * Returned by asynchronous functions. Must not be free'd by
907@@ -105,7 +106,7 @@
908
909 /**
910 * Callback called when a request for client file descriptors completes
911- * \param [in] connection The connection associated with the display change
912+ * \param [in] prompt_session The prompt session
913 * \param [in] count The number of FDs allocated
914 * \param [in] fds Array of FDs
915 * \param [in,out] context The context provided by client
916@@ -114,7 +115,7 @@
917 */
918
919 typedef void (*mir_client_fd_callback)(
920- MirConnection* connection, size_t count, int const* fds, void* context);
921+ MirPromptSession *prompt_session, size_t count, int const* fds, void* context);
922
923 /**
924 * MirBufferUsage specifies how a surface can and will be used. A "hardware"
925@@ -319,6 +320,31 @@
926 */
927 typedef void (*mir_screencast_callback)(MirScreencast *screencast, void *client_context);
928
929+/**
930+ * Callback member of MirPromptSession for handling of prompt sessions.
931+ * \param [in] prompt_provider The prompt session associated with the callback
932+ * \param [in,out] context The context provided by the client
933+ */
934+typedef void (*mir_prompt_session_callback)(MirPromptSession* prompt_provider, void* context);
935+
936+/**
937+ * Callback member of MirPromptSession for adding prompt providers
938+ * \param [in] prompt_provider The prompt session associated with the callback
939+ * \param [in] added True if the session was added, false otherwise
940+ * \param [in,out] context The context provided by the client
941+ */
942+typedef void (*mir_prompt_session_add_prompt_provider_callback)(
943+ MirPromptSession* prompt_provider, MirBool added, void* context);
944+
945+/**
946+ * Callback member of MirPromptSession for handling of prompt sessions events.
947+ * \param [in] prompt_provider The prompt session associated with the callback
948+ * \param [in] state The state of the prompt session
949+ * \param [in,out] context The context provided by the client
950+ */
951+typedef void (*mir_prompt_session_state_change_callback)(
952+ MirPromptSession* prompt_provider, MirPromptSessionState state, void* context);
953+
954 #ifdef __cplusplus
955 }
956 /**@}*/
957
958=== modified file 'include/shared/mir_toolkit/common.h'
959--- include/shared/mir_toolkit/common.h 2014-06-09 17:16:32 +0000
960+++ include/shared/mir_toolkit/common.h 2014-06-16 15:01:17 +0000
961@@ -88,6 +88,12 @@
962 mir_power_mode_off /* Powered down. */
963 } MirPowerMode;
964
965+typedef enum MirPromptSessionState
966+{
967+ mir_prompt_session_state_stopped = 0,
968+ mir_prompt_session_state_started
969+} MirPromptSessionState;
970+
971 /**
972 * The order of components in a format enum matches the
973 * order of the components as they would be written in an
974
975=== modified file 'include/shared/mir_toolkit/event.h'
976--- include/shared/mir_toolkit/event.h 2014-03-06 06:05:17 +0000
977+++ include/shared/mir_toolkit/event.h 2014-06-16 15:01:17 +0000
978@@ -40,7 +40,8 @@
979 mir_event_type_key,
980 mir_event_type_motion,
981 mir_event_type_surface,
982- mir_event_type_resize
983+ mir_event_type_resize,
984+ mir_event_type_prompt_session_state_change
985 } MirEventType;
986
987 typedef enum {
988@@ -204,6 +205,13 @@
989 int height;
990 } MirResizeEvent;
991
992+typedef struct
993+{
994+ MirEventType type;
995+
996+ MirPromptSessionState new_state;
997+} MirPromptSessionEvent;
998+
999 typedef union
1000 {
1001 MirEventType type;
1002@@ -211,6 +219,7 @@
1003 MirMotionEvent motion;
1004 MirSurfaceEvent surface;
1005 MirResizeEvent resize;
1006+ MirPromptSessionEvent prompt_session;
1007 } MirEvent;
1008
1009 #ifdef __cplusplus
1010
1011=== modified file 'include/test/mir_test/test_protobuf_client.h'
1012--- include/test/mir_test/test_protobuf_client.h 2013-08-28 03:41:48 +0000
1013+++ include/test/mir_test/test_protobuf_client.h 2014-06-16 15:01:17 +0000
1014@@ -21,6 +21,7 @@
1015 #define MIR_TEST_TEST_CLIENT_H_
1016
1017 #include "mir_protobuf.pb.h"
1018+#include "wait_condition.h"
1019
1020 #include <gmock/gmock.h>
1021
1022@@ -49,6 +50,9 @@
1023 mir::protobuf::Connection connection;
1024 mir::protobuf::DisplayConfiguration disp_config;
1025 mir::protobuf::DisplayConfiguration disp_config_response;
1026+ mir::protobuf::PromptSessionParameters prompt_session_parameters;
1027+ mir::protobuf::PromptProvider prompt_provider;
1028+ mir::protobuf::Void prompt_session;
1029
1030 MOCK_METHOD0(connect_done, void());
1031 MOCK_METHOD0(create_surface_done, void());
1032@@ -57,6 +61,9 @@
1033 MOCK_METHOD0(disconnect_done, void());
1034 MOCK_METHOD0(drm_auth_magic_done, void());
1035 MOCK_METHOD0(display_configure_done, void());
1036+ MOCK_METHOD0(prompt_session_start_done, void());
1037+ MOCK_METHOD0(prompt_session_add_prompt_provider_done, void());
1038+ MOCK_METHOD0(prompt_session_stop_done, void());
1039
1040 void on_connect_done();
1041
1042@@ -94,6 +101,12 @@
1043
1044 void wait_for_configure_display_done();
1045
1046+ void wait_for_prompt_session_start_done();
1047+
1048+ void wait_for_prompt_session_add_prompt_provider_done();
1049+
1050+ void wait_for_prompt_session_stop_done();
1051+
1052 const int maxwait;
1053 std::atomic<bool> connect_done_called;
1054 std::atomic<bool> create_surface_called;
1055@@ -104,6 +117,10 @@
1056 std::atomic<bool> configure_display_done_called;
1057 std::atomic<bool> tfd_done_called;
1058
1059+ WaitCondition wc_prompt_session_start;
1060+ WaitCondition wc_prompt_session_add;
1061+ WaitCondition wc_prompt_session_stop;
1062+
1063 std::atomic<int> connect_done_count;
1064 std::atomic<int> create_surface_done_count;
1065 std::atomic<int> disconnect_done_count;
1066
1067=== added file 'include/test/mir_test_doubles/mock_prompt_session_listener.h'
1068--- include/test/mir_test_doubles/mock_prompt_session_listener.h 1970-01-01 00:00:00 +0000
1069+++ include/test/mir_test_doubles/mock_prompt_session_listener.h 2014-06-16 15:01:17 +0000
1070@@ -0,0 +1,48 @@
1071+/*
1072+ * Copyright © 2014 Canonical Ltd.
1073+ *
1074+ * This program is free software: you can redistribute it and/or modify it
1075+ * under the terms of the GNU General Public License version 3,
1076+ * as published by the Free Software Foundation.
1077+ *
1078+ * This program is distributed in the hope that it will be useful,
1079+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1080+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1081+ * GNU General Public License for more details.
1082+ *
1083+ * You should have received a copy of the GNU General Public License
1084+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1085+ *
1086+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
1087+ */
1088+
1089+#ifndef MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_
1090+#define MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_
1091+
1092+#include "mir/scene/prompt_session_listener.h"
1093+
1094+#include <gmock/gmock.h>
1095+
1096+namespace mir
1097+{
1098+namespace test
1099+{
1100+namespace doubles
1101+{
1102+
1103+struct MockPromptSessionListener : public scene::PromptSessionListener
1104+{
1105+ virtual ~MockPromptSessionListener() noexcept(true) {}
1106+
1107+ MOCK_METHOD1(starting, void(std::shared_ptr<scene::PromptSession> const&));
1108+ MOCK_METHOD1(stopping, void(std::shared_ptr<scene::PromptSession> const&));
1109+
1110+ MOCK_METHOD2(prompt_provider_added, void(scene::PromptSession const&, std::shared_ptr<scene::Session> const&));
1111+ MOCK_METHOD2(prompt_provider_removed, void(scene::PromptSession const&, std::shared_ptr<scene::Session> const&));
1112+};
1113+
1114+}
1115+}
1116+} // namespace mir
1117+
1118+#endif // MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_
1119
1120=== modified file 'include/test/mir_test_doubles/mock_scene_session.h'
1121--- include/test/mir_test_doubles/mock_scene_session.h 2014-06-02 17:07:02 +0000
1122+++ include/test/mir_test_doubles/mock_scene_session.h 2014-06-16 15:01:17 +0000
1123@@ -52,6 +52,9 @@
1124 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));
1125
1126 MOCK_METHOD1(set_lifecycle_state, void(MirLifecycleState state));
1127+
1128+ MOCK_METHOD0(start_prompt_session, void());
1129+ MOCK_METHOD0(stop_prompt_session, void());
1130 };
1131
1132 }
1133
1134=== modified file 'include/test/mir_test_doubles/mock_shell.h'
1135--- include/test/mir_test_doubles/mock_shell.h 2014-04-15 05:31:19 +0000
1136+++ include/test/mir_test_doubles/mock_shell.h 2014-06-16 15:01:17 +0000
1137@@ -22,6 +22,7 @@
1138 #include "mir/scene/surface_creation_parameters.h"
1139 #include "mir/frontend/shell.h"
1140 #include "mir/frontend/surface_id.h"
1141+#include "mir/scene/prompt_session_creation_parameters.h"
1142
1143 #include <gmock/gmock.h>
1144
1145@@ -43,6 +44,17 @@
1146
1147 MOCK_METHOD2(create_surface_for, frontend::SurfaceId(std::shared_ptr<frontend::Session> const&, scene::SurfaceCreationParameters const&));
1148 MOCK_METHOD1(handle_surface_created, void(std::shared_ptr<frontend::Session> const&));
1149+
1150+ MOCK_METHOD2(start_prompt_session_for, std::shared_ptr<frontend::PromptSession>(
1151+ std::shared_ptr<frontend::Session> const&,
1152+ scene::PromptSessionCreationParameters const&));
1153+ MOCK_METHOD2(add_prompt_provider_process_for, void(
1154+ std::shared_ptr<frontend::PromptSession> const&,
1155+ pid_t));
1156+ MOCK_METHOD2(add_prompt_provider_for, void(
1157+ std::shared_ptr<frontend::PromptSession> const&,
1158+ std::shared_ptr<frontend::Session> const&));
1159+ MOCK_METHOD1(stop_prompt_session, void(std::shared_ptr<frontend::PromptSession> const&));
1160 };
1161
1162 }
1163
1164=== added file 'include/test/mir_test_doubles/null_client_event_sink.h'
1165--- include/test/mir_test_doubles/null_client_event_sink.h 1970-01-01 00:00:00 +0000
1166+++ include/test/mir_test_doubles/null_client_event_sink.h 2014-06-16 15:01:17 +0000
1167@@ -0,0 +1,42 @@
1168+/*
1169+ * Copyright © 2012 Canonical Ltd.
1170+ *
1171+ * This program is free software: you can redistribute it and/or modify it
1172+ * under the terms of the GNU General Public License version 3,
1173+ * as published by the Free Software Foundation.
1174+ *
1175+ * This program is distributed in the hope that it will be useful,
1176+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1177+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1178+ * GNU General Public License for more details.
1179+ *
1180+ * You should have received a copy of the GNU General Public License
1181+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1182+ *
1183+ * Authored by: Nick Dedekink <nick.dedekind@canonical.com>
1184+ */
1185+
1186+#ifndef MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_
1187+#define MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_
1188+
1189+#include "src/client/event_sink.h"
1190+
1191+#include <gmock/gmock.h>
1192+
1193+namespace mir
1194+{
1195+namespace test
1196+{
1197+namespace doubles
1198+{
1199+class NullClientEventSink : public client::EventSink
1200+{
1201+public:
1202+ void handle_event(MirEvent const&) override {}
1203+};
1204+
1205+}
1206+}
1207+}
1208+
1209+#endif /* MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_ */
1210
1211=== added file 'include/test/mir_test_doubles/null_prompt_session.h'
1212--- include/test/mir_test_doubles/null_prompt_session.h 1970-01-01 00:00:00 +0000
1213+++ include/test/mir_test_doubles/null_prompt_session.h 2014-06-16 15:01:17 +0000
1214@@ -0,0 +1,37 @@
1215+/*
1216+ * Copyright © 2013-2014 Canonical Ltd.
1217+ *
1218+ * This program is free software: you can redistribute it and/or modify it
1219+ * under the terms of the GNU General Public License version 3,
1220+ * as published by the Free Software Foundation.
1221+ *
1222+ * This program is distributed in the hope that it will be useful,
1223+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1224+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1225+ * GNU General Public License for more details.
1226+ *
1227+ * You should have received a copy of the GNU General Public License
1228+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1229+ *
1230+ * Authored by: Nick Dedekind <nick.dedekind@gmail.com>
1231+ */
1232+
1233+#ifndef MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_
1234+#define MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_
1235+
1236+#include "mir/scene/prompt_session.h"
1237+
1238+namespace mir
1239+{
1240+namespace test
1241+{
1242+namespace doubles
1243+{
1244+class NullPromptSession : public scene::PromptSession
1245+{
1246+};
1247+}
1248+}
1249+}
1250+
1251+#endif /* MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_ */
1252
1253=== added file 'include/test/mir_test_doubles/null_prompt_session_manager.h'
1254--- include/test/mir_test_doubles/null_prompt_session_manager.h 1970-01-01 00:00:00 +0000
1255+++ include/test/mir_test_doubles/null_prompt_session_manager.h 2014-06-16 15:01:17 +0000
1256@@ -0,0 +1,82 @@
1257+/*
1258+ * Copyright © 2014 Canonical Ltd.
1259+ *
1260+ * This program is free software: you can redistribute it and/or modify it
1261+ * under the terms of the GNU General Public License version 3,
1262+ * as published by the Free Software Foundation.
1263+ *
1264+ * This program is distributed in the hope that it will be useful,
1265+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1266+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1267+ * GNU General Public License for more details.
1268+ *
1269+ * You should have received a copy of the GNU General Public License
1270+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1271+ *
1272+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
1273+ */
1274+
1275+#ifndef MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_
1276+#define MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_
1277+
1278+#include "mir/scene/prompt_session_manager.h"
1279+
1280+namespace mir
1281+{
1282+namespace test
1283+{
1284+namespace doubles
1285+{
1286+
1287+class NullPromptSessionManager: public scene::PromptSessionManager
1288+{
1289+public:
1290+ std::shared_ptr<scene::PromptSession> start_prompt_session_for(std::shared_ptr<scene::Session> const&,
1291+ scene::PromptSessionCreationParameters const&) const
1292+ {
1293+ return std::shared_ptr<scene::PromptSession>();
1294+ }
1295+
1296+ void stop_prompt_session(std::shared_ptr<scene::PromptSession> const&) const
1297+ {
1298+ }
1299+
1300+ void add_prompt_provider(std::shared_ptr<scene::PromptSession> const&,
1301+ std::shared_ptr<scene::Session> const&) const
1302+ {
1303+ }
1304+
1305+ void add_prompt_provider_by_pid(std::shared_ptr<scene::PromptSession> const&,
1306+ pid_t) const
1307+ {
1308+ }
1309+
1310+ void add_expected_session(std::shared_ptr<scene::Session> const&) const
1311+ {
1312+ }
1313+
1314+ void remove_session(std::shared_ptr<scene::Session> const&) const
1315+ {
1316+ }
1317+
1318+ std::shared_ptr<scene::Session> application_for(std::shared_ptr<scene::PromptSession> const&) const
1319+ {
1320+ return std::shared_ptr<scene::Session>();
1321+ }
1322+
1323+ std::shared_ptr<scene::Session> helper_for(std::shared_ptr<scene::PromptSession> const&) const
1324+ {
1325+ return std::shared_ptr<scene::Session>();
1326+ }
1327+
1328+ void for_each_provider_in(std::shared_ptr<scene::PromptSession> const&,
1329+ std::function<void(std::shared_ptr<scene::Session> const&)> const&) const
1330+ {
1331+ }
1332+};
1333+
1334+}
1335+}
1336+}
1337+
1338+#endif // MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_
1339
1340=== modified file 'include/test/mir_test_doubles/stub_scene_session.h'
1341--- include/test/mir_test_doubles/stub_scene_session.h 2014-06-02 17:07:02 +0000
1342+++ include/test/mir_test_doubles/stub_scene_session.h 2014-06-16 15:01:17 +0000
1343@@ -30,6 +30,8 @@
1344
1345 struct StubSceneSession : public scene::Session
1346 {
1347+ StubSceneSession(pid_t pid = -1) : pid(pid) {}
1348+
1349 frontend::SurfaceId create_surface(scene::SurfaceCreationParameters const& /* params */) override
1350 {
1351 return frontend::SurfaceId{0};
1352@@ -45,9 +47,10 @@
1353 {
1354 return std::string();
1355 }
1356+
1357 pid_t process_id() const override
1358 {
1359- return -1;
1360+ return pid;
1361 }
1362
1363 void force_requests_to_complete() override
1364@@ -76,6 +79,16 @@
1365 void set_lifecycle_state(MirLifecycleState /*state*/)
1366 {
1367 }
1368+
1369+ void start_prompt_session() override
1370+ {
1371+ }
1372+
1373+ void stop_prompt_session() override
1374+ {
1375+ }
1376+
1377+ pid_t const pid;
1378 };
1379
1380 }
1381
1382=== modified file 'include/test/mir_test_doubles/stub_shell.h'
1383--- include/test/mir_test_doubles/stub_shell.h 2014-04-15 05:31:19 +0000
1384+++ include/test/mir_test_doubles/stub_shell.h 2014-06-16 15:01:17 +0000
1385@@ -49,6 +49,25 @@
1386 void handle_surface_created(std::shared_ptr<frontend::Session> const& /* session */) override
1387 {
1388 }
1389+ std::shared_ptr<frontend::PromptSession> start_prompt_session_for(std::shared_ptr<frontend::Session> const& /* session */,
1390+ scene::PromptSessionCreationParameters const& /* params */)
1391+ {
1392+ return std::shared_ptr<frontend::PromptSession>();
1393+ }
1394+ void add_prompt_provider_process_for(
1395+ std::shared_ptr<frontend::PromptSession> const& /* prompt_session */,
1396+ pid_t /* process_id */)
1397+ {
1398+ }
1399+ void add_prompt_provider_for(
1400+ std::shared_ptr<frontend::PromptSession> const& /* prompt_session */,
1401+ std::shared_ptr<frontend::Session> const& /* session */)
1402+ {
1403+ }
1404+ void stop_prompt_session(std::shared_ptr<frontend::PromptSession> const& /* prompt_session */)
1405+ {
1406+ }
1407+
1408 std::shared_ptr<StubSession> const stub_session;
1409 };
1410
1411
1412=== modified file 'src/client/CMakeLists.txt'
1413--- src/client/CMakeLists.txt 2014-06-02 17:07:02 +0000
1414+++ src/client/CMakeLists.txt 2014-06-16 15:01:17 +0000
1415@@ -48,6 +48,9 @@
1416 mir_screencast.cpp
1417 mir_screencast_api.cpp
1418 mir_cursor_api.cpp
1419+ mir_prompt_session.cpp
1420+ mir_prompt_session_api.cpp
1421+ mir_event_distributor.cpp
1422 )
1423
1424 add_library(
1425@@ -82,7 +85,7 @@
1426 mirclientlttngstatic
1427
1428 ${MIR_COMMON_PLATFORM_LIBRARIES}
1429-
1430+
1431 3rd_party
1432 )
1433
1434
1435=== modified file 'src/client/connection_configuration.h'
1436--- src/client/connection_configuration.h 2014-03-06 06:05:17 +0000
1437+++ src/client/connection_configuration.h 2014-06-16 15:01:17 +0000
1438@@ -47,6 +47,8 @@
1439 class ClientPlatformFactory;
1440 class DisplayConfiguration;
1441 class LifecycleControl;
1442+class EventSink;
1443+class EventHandlerRegister;
1444
1445 class ConnectionConfiguration
1446 {
1447@@ -60,6 +62,8 @@
1448 virtual std::shared_ptr<input::receiver::InputPlatform> the_input_platform() = 0;
1449 virtual std::shared_ptr<DisplayConfiguration> the_display_configuration() = 0;
1450 virtual std::shared_ptr<LifecycleControl> the_lifecycle_control() = 0;
1451+ virtual std::shared_ptr<EventSink> the_event_sink() = 0;
1452+ virtual std::shared_ptr<EventHandlerRegister> the_event_handler_register() = 0;
1453
1454 protected:
1455 ConnectionConfiguration() = default;
1456
1457=== modified file 'src/client/default_connection_configuration.cpp'
1458--- src/client/default_connection_configuration.cpp 2014-03-26 05:48:59 +0000
1459+++ src/client/default_connection_configuration.cpp 2014-06-16 15:01:17 +0000
1460@@ -32,6 +32,7 @@
1461 #include "lifecycle_control.h"
1462 #include "mir/shared_library.h"
1463 #include "client_platform_factory.h"
1464+#include "mir_event_distributor.h"
1465
1466 namespace mcl = mir::client;
1467
1468@@ -81,7 +82,7 @@
1469 [this]
1470 {
1471 return mcl::rpc::make_rpc_channel(
1472- the_socket_file(), the_surface_map(), the_display_configuration(), the_rpc_report(), the_lifecycle_control());
1473+ the_socket_file(), the_surface_map(), the_display_configuration(), the_rpc_report(), the_lifecycle_control(), the_event_sink());
1474 });
1475 }
1476
1477@@ -182,3 +183,21 @@
1478 return std::make_shared<mcl::LifecycleControl>();
1479 });
1480 }
1481+
1482+std::shared_ptr<mcl::EventSink> mcl::DefaultConnectionConfiguration::the_event_sink()
1483+{
1484+ return event_distributor(
1485+ []
1486+ {
1487+ return std::make_shared<MirEventDistributor>();
1488+ });
1489+}
1490+
1491+std::shared_ptr<mcl::EventHandlerRegister> mcl::DefaultConnectionConfiguration::the_event_handler_register()
1492+{
1493+ return event_distributor(
1494+ []
1495+ {
1496+ return std::make_shared<MirEventDistributor>();
1497+ });
1498+}
1499
1500=== modified file 'src/client/default_connection_configuration.h'
1501--- src/client/default_connection_configuration.h 2014-03-06 06:05:17 +0000
1502+++ src/client/default_connection_configuration.h 2014-06-16 15:01:17 +0000
1503@@ -36,6 +36,7 @@
1504 }
1505 namespace client
1506 {
1507+class EventDistributor;
1508
1509 namespace rpc
1510 {
1511@@ -54,6 +55,8 @@
1512 std::shared_ptr<input::receiver::InputPlatform> the_input_platform();
1513 std::shared_ptr<DisplayConfiguration> the_display_configuration();
1514 std::shared_ptr<LifecycleControl> the_lifecycle_control();
1515+ std::shared_ptr<EventSink> the_event_sink();
1516+ std::shared_ptr<EventHandlerRegister> the_event_handler_register();
1517
1518 virtual std::string the_socket_file();
1519 virtual std::shared_ptr<rpc::RpcReport> the_rpc_report();
1520@@ -67,6 +70,7 @@
1521 CachedPtr<ConnectionSurfaceMap> surface_map;
1522 CachedPtr<DisplayConfiguration> display_configuration;
1523 CachedPtr<LifecycleControl> lifecycle_control;
1524+ CachedPtr<EventDistributor> event_distributor;
1525
1526 CachedPtr<rpc::RpcReport> rpc_report;
1527 CachedPtr<input::receiver::InputReceiverReport> input_receiver_report;
1528
1529=== added file 'src/client/event_distributor.h'
1530--- src/client/event_distributor.h 1970-01-01 00:00:00 +0000
1531+++ src/client/event_distributor.h 2014-06-16 15:01:17 +0000
1532@@ -0,0 +1,37 @@
1533+/*
1534+ * Copyright © 2014 Canonical Ltd.
1535+ *
1536+ * This program is free software: you can redistribute it and/or modify it
1537+ * under the terms of the GNU Lesser General Public License version 3,
1538+ * as published by the Free Software Foundation.
1539+ *
1540+ * This program is distributed in the hope that it will be useful,
1541+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1542+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1543+ * GNU Lesser General Public License for more details.
1544+ *
1545+ * You should have received a copy of the GNU Lesser General Public License
1546+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1547+ *
1548+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1549+ */
1550+
1551+#ifndef MIR_CLIENT_EVENT_DISTRIBUTOR_H_
1552+#define MIR_CLIENT_EVENT_DISTRIBUTOR_H_
1553+
1554+#include "event_sink.h"
1555+#include "event_handler_register.h"
1556+
1557+namespace mir
1558+{
1559+namespace client
1560+{
1561+
1562+class EventDistributor : public EventHandlerRegister, public EventSink
1563+{
1564+};
1565+
1566+} // namespace client
1567+} // namespace mir
1568+
1569+#endif // MIR_CLIENT_EVENT_DISTRIBUTOR_H_
1570
1571=== added file 'src/client/event_handler_register.h'
1572--- src/client/event_handler_register.h 1970-01-01 00:00:00 +0000
1573+++ src/client/event_handler_register.h 2014-06-16 15:01:17 +0000
1574@@ -0,0 +1,47 @@
1575+/*
1576+ * Copyright © 2014 Canonical Ltd.
1577+ *
1578+ * This program is free software: you can redistribute it and/or modify it
1579+ * under the terms of the GNU Lesser General Public License version 3,
1580+ * as published by the Free Software Foundation.
1581+ *
1582+ * This program is distributed in the hope that it will be useful,
1583+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1584+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1585+ * GNU Lesser General Public License for more details.
1586+ *
1587+ * You should have received a copy of the GNU Lesser General Public License
1588+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1589+ *
1590+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1591+ */
1592+
1593+#ifndef MIR_CLIENT_EVENT_HANDLER_REGISTER_H_
1594+#define MIR_CLIENT_EVENT_HANDLER_REGISTER_H_
1595+
1596+#include "mir_toolkit/event.h"
1597+
1598+#include <functional>
1599+
1600+namespace mir
1601+{
1602+namespace client
1603+{
1604+
1605+class EventHandlerRegister
1606+{
1607+public:
1608+ virtual ~EventHandlerRegister() = default;
1609+
1610+ virtual int register_event_handler(std::function<void(MirEvent const&)> const&) = 0;
1611+ virtual void unregister_event_handler(int id) = 0;
1612+
1613+protected:
1614+ EventHandlerRegister() = default;
1615+ EventHandlerRegister(EventHandlerRegister const&) = delete;
1616+ EventHandlerRegister& operator=(EventHandlerRegister const&) = delete;
1617+};
1618+} // namespace client
1619+} // namespace mir
1620+
1621+#endif // MIR_CLIENT_EVENT_HANDLER_REGISTER_H_
1622
1623=== added file 'src/client/event_sink.h'
1624--- src/client/event_sink.h 1970-01-01 00:00:00 +0000
1625+++ src/client/event_sink.h 2014-06-16 15:01:17 +0000
1626@@ -0,0 +1,44 @@
1627+/*
1628+ * Copyright © 2014 Canonical Ltd.
1629+ *
1630+ * This program is free software: you can redistribute it and/or modify it
1631+ * under the terms of the GNU Lesser General Public License version 3,
1632+ * as published by the Free Software Foundation.
1633+ *
1634+ * This program is distributed in the hope that it will be useful,
1635+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1636+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1637+ * GNU Lesser General Public License for more details.
1638+ *
1639+ * You should have received a copy of the GNU Lesser General Public License
1640+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1641+ *
1642+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1643+ */
1644+
1645+#ifndef MIR_CLIENT_EVENT_SINK_H_
1646+#define MIR_CLIENT_EVENT_SINK_H_
1647+
1648+#include "mir_toolkit/event.h"
1649+
1650+namespace mir
1651+{
1652+namespace client
1653+{
1654+
1655+class EventSink
1656+{
1657+public:
1658+ virtual ~EventSink() = default;
1659+
1660+ virtual void handle_event(MirEvent const& e) = 0;
1661+
1662+protected:
1663+ EventSink() = default;
1664+ EventSink(EventSink const&) = delete;
1665+ EventSink& operator=(EventSink const&) = delete;
1666+};
1667+} // namespace client
1668+} // namespace mir
1669+
1670+#endif // MIR_CLIENT_EVENT_SINK_H_
1671
1672=== modified file 'src/client/mir_connection.cpp'
1673--- src/client/mir_connection.cpp 2014-06-11 16:24:02 +0000
1674+++ src/client/mir_connection.cpp 2014-06-16 15:01:17 +0000
1675@@ -18,6 +18,7 @@
1676
1677 #include "mir_connection.h"
1678 #include "mir_surface.h"
1679+#include "mir_prompt_session.h"
1680 #include "client_platform.h"
1681 #include "client_platform_factory.h"
1682 #include "rpc/mir_basic_rpc_channel.h"
1683@@ -82,7 +83,8 @@
1684 input_platform(conf.the_input_platform()),
1685 display_configuration(conf.the_display_configuration()),
1686 lifecycle_control(conf.the_lifecycle_control()),
1687- surface_map(conf.the_surface_map())
1688+ surface_map(conf.the_surface_map()),
1689+ event_handler_register(conf.the_event_handler_register())
1690 {
1691 connect_result.set_error("connect not called");
1692 {
1693@@ -194,6 +196,11 @@
1694 return new_wait_handle;
1695 }
1696
1697+MirPromptSession* MirConnection::create_prompt_session()
1698+{
1699+ return new MirPromptSession(display_server(), event_handler_register);
1700+}
1701+
1702 namespace
1703 {
1704 void default_lifecycle_event_handler(MirLifecycleState transition)
1705@@ -307,40 +314,6 @@
1706 return &drm_auth_magic_wait_handle;
1707 }
1708
1709-MirWaitHandle* MirConnection::new_fds_for_trusted_clients(
1710- unsigned int no_of_fds,
1711- mir_client_fd_callback callback,
1712- void * context)
1713-{
1714- mir::protobuf::SocketFDRequest request;
1715- request.set_number(no_of_fds);
1716-
1717- server.new_fds_for_trusted_clients(
1718- nullptr,
1719- &request,
1720- &socket_fd_response,
1721- google::protobuf::NewCallback(this, &MirConnection::done_fds_for_trusted_clients,
1722- callback, context));
1723-
1724- return &fds_for_trusted_clients_wait_handle;
1725-}
1726-
1727-void MirConnection::done_fds_for_trusted_clients(
1728- mir_client_fd_callback callback,
1729- void* context)
1730-{
1731- auto const size = socket_fd_response.fd_size();
1732-
1733- std::vector<int> fds;
1734- fds.reserve(size);
1735-
1736- for (auto i = 0; i != size; ++i)
1737- fds.push_back(socket_fd_response.fd(i));
1738-
1739- callback(this, size, fds.data(), context);
1740- fds_for_trusted_clients_wait_handle.result_received();
1741-}
1742-
1743 bool MirConnection::is_valid(MirConnection *connection)
1744 {
1745 {
1746@@ -526,6 +499,6 @@
1747 }
1748
1749 mir::protobuf::DisplayServer& MirConnection::display_server()
1750-{
1751+{
1752 return server;
1753 }
1754
1755=== modified file 'src/client/mir_connection.h'
1756--- src/client/mir_connection.h 2014-06-02 17:07:02 +0000
1757+++ src/client/mir_connection.h 2014-06-16 15:01:17 +0000
1758@@ -45,6 +45,7 @@
1759 class ConnectionSurfaceMap;
1760 class DisplayConfiguration;
1761 class LifecycleControl;
1762+class EventHandlerRegister;
1763
1764 namespace rpc
1765 {
1766@@ -86,6 +87,8 @@
1767 mir_surface_callback callback,
1768 void *context);
1769
1770+ MirPromptSession* create_prompt_session();
1771+
1772 char const * get_error_message();
1773
1774 MirWaitHandle* connect(
1775@@ -123,11 +126,6 @@
1776
1777 bool set_extra_platform_data(std::vector<int> const& extra_platform_data);
1778
1779- MirWaitHandle* new_fds_for_trusted_clients(
1780- unsigned int no_of_fds,
1781- mir_client_fd_callback callback,
1782- void * context);
1783-
1784 std::shared_ptr<google::protobuf::RpcChannel> rpc_channel() const
1785 {
1786 return channel;
1787@@ -147,7 +145,6 @@
1788 mir::protobuf::ConnectParameters connect_parameters;
1789 mir::protobuf::DRMAuthMagicStatus drm_auth_magic_status;
1790 mir::protobuf::DisplayConfiguration display_configuration_response;
1791- mir::protobuf::SocketFD socket_fd_response;
1792
1793 std::shared_ptr<mir::client::ClientPlatformFactory> const client_platform_factory;
1794 std::shared_ptr<mir::client::ClientPlatform> platform;
1795@@ -161,7 +158,6 @@
1796 MirWaitHandle disconnect_wait_handle;
1797 MirWaitHandle drm_auth_magic_wait_handle;
1798 MirWaitHandle configure_display_wait_handle;
1799- MirWaitHandle fds_for_trusted_clients_wait_handle;
1800
1801 std::mutex release_wait_handle_guard;
1802 std::vector<MirWaitHandle*> release_wait_handles;
1803@@ -172,6 +168,8 @@
1804
1805 std::shared_ptr<mir::client::ConnectionSurfaceMap> const surface_map;
1806
1807+ std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;
1808+
1809 std::vector<int> extra_platform_data;
1810
1811 struct SurfaceRelease;
1812@@ -181,7 +179,6 @@
1813 void connected(mir_connected_callback callback, void * context);
1814 void released(SurfaceRelease );
1815 void done_drm_auth_magic(mir_drm_auth_magic_callback callback, void* context);
1816- void done_fds_for_trusted_clients(mir_client_fd_callback callback, void* context);
1817 bool validate_user_display_config(MirDisplayConfiguration* config);
1818 };
1819
1820
1821=== modified file 'src/client/mir_connection_api.cpp'
1822--- src/client/mir_connection_api.cpp 2014-06-02 17:07:02 +0000
1823+++ src/client/mir_connection_api.cpp 2014-06-16 15:01:17 +0000
1824@@ -272,24 +272,6 @@
1825 }
1826 }
1827
1828-MirWaitHandle* mir_connection_new_fds_for_trusted_clients(
1829- MirConnection* connection,
1830- unsigned int no_of_fds,
1831- mir_client_fd_callback callback,
1832- void * context)
1833-{
1834- try
1835- {
1836- return connection ?
1837- connection->new_fds_for_trusted_clients(no_of_fds, callback, context) :
1838- nullptr;
1839- }
1840- catch (std::exception const&)
1841- {
1842- return nullptr;
1843- }
1844-}
1845-
1846 MirEGLNativeDisplayType mir_connection_get_egl_native_display(
1847 MirConnection* connection)
1848 {
1849
1850=== added file 'src/client/mir_event_distributor.cpp'
1851--- src/client/mir_event_distributor.cpp 1970-01-01 00:00:00 +0000
1852+++ src/client/mir_event_distributor.cpp 2014-06-16 15:01:17 +0000
1853@@ -0,0 +1,56 @@
1854+/*
1855+ * Copyright © 2014 Canonical Ltd.
1856+ *
1857+ * This program is free software: you can redistribute it and/or modify it
1858+ * under the terms of the GNU Lesser General Public License version 3,
1859+ * as published by the Free Software Foundation.
1860+ *
1861+ * This program is distributed in the hope that it will be useful,
1862+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1863+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1864+ * GNU Lesser General Public License for more details.
1865+ *
1866+ * You should have received a copy of the GNU Lesser General Public License
1867+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1868+ *
1869+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1870+ */
1871+
1872+#include "mir_event_distributor.h"
1873+
1874+MirEventDistributor::MirEventDistributor() :
1875+ next_fn_id{0}
1876+{
1877+}
1878+
1879+int MirEventDistributor::register_event_handler(std::function<void(MirEvent const&)> const& fn)
1880+{
1881+ std::unique_lock<decltype(mutex)> lock(mutex);
1882+
1883+ int id = ++next_fn_id;
1884+ event_handlers[id] = fn;
1885+ return id;
1886+}
1887+
1888+void MirEventDistributor::unregister_event_handler(int id)
1889+{
1890+ std::unique_lock<decltype(mutex)> lock(mutex);
1891+
1892+ event_handlers.erase(id);
1893+}
1894+
1895+void MirEventDistributor::handle_event(MirEvent const& event)
1896+{
1897+ std::unique_lock<decltype(mutex)> lock(mutex);
1898+
1899+ auto const event_handlers_copy(event_handlers);
1900+
1901+ for (auto const& handler : event_handlers_copy)
1902+ {
1903+ // Ensure handler wasn't unregistered since making copy
1904+ if (event_handlers.find(handler.first) != event_handlers.end())
1905+ {
1906+ handler.second(event);
1907+ }
1908+ }
1909+}
1910
1911=== added file 'src/client/mir_event_distributor.h'
1912--- src/client/mir_event_distributor.h 1970-01-01 00:00:00 +0000
1913+++ src/client/mir_event_distributor.h 2014-06-16 15:01:17 +0000
1914@@ -0,0 +1,43 @@
1915+/*
1916+ * Copyright © 2014 Canonical Ltd.
1917+ *
1918+ * This program is free software: you can redistribute it and/or modify it
1919+ * under the terms of the GNU Lesser General Public License version 3,
1920+ * as published by the Free Software Foundation.
1921+ *
1922+ * This program is distributed in the hope that it will be useful,
1923+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1924+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1925+ * GNU Lesser General Public License for more details.
1926+ *
1927+ * You should have received a copy of the GNU Lesser General Public License
1928+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1929+ *
1930+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1931+ */
1932+
1933+#ifndef MIR_EVENT_DISTRIBUTOR_H
1934+
1935+#include "event_distributor.h"
1936+
1937+#include <mutex>
1938+#include <map>
1939+
1940+class MirEventDistributor : public mir::client::EventDistributor
1941+{
1942+public:
1943+ MirEventDistributor();
1944+ ~MirEventDistributor() = default;
1945+
1946+ int register_event_handler(std::function<void(MirEvent const&)> const&) override;
1947+ void unregister_event_handler(int id) override;
1948+
1949+ void handle_event(MirEvent const& event) override;
1950+
1951+private:
1952+ mutable std::recursive_mutex mutex;
1953+ std::map<int, std::function<void(MirEvent const&)>> event_handlers;
1954+ int next_fn_id;
1955+};
1956+
1957+#endif /* MIR_EVENT_DISTRIBUTOR_H */
1958
1959=== added file 'src/client/mir_prompt_session.cpp'
1960--- src/client/mir_prompt_session.cpp 1970-01-01 00:00:00 +0000
1961+++ src/client/mir_prompt_session.cpp 2014-06-16 15:01:17 +0000
1962@@ -0,0 +1,203 @@
1963+/*
1964+ * Copyright © 2014 Canonical Ltd.
1965+ *
1966+ * This program is free software: you can redistribute it and/or modify it
1967+ * under the terms of the GNU Lesser General Public License version 3,
1968+ * as published by the Free Software Foundation.
1969+ *
1970+ * This program is distributed in the hope that it will be useful,
1971+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1972+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1973+ * GNU Lesser General Public License for more details.
1974+ *
1975+ * You should have received a copy of the GNU Lesser General Public License
1976+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1977+ *
1978+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1979+ */
1980+
1981+#include "mir_prompt_session.h"
1982+#include "event_handler_register.h"
1983+
1984+namespace mp = mir::protobuf;
1985+namespace mcl = mir::client;
1986+
1987+MirPromptSession::MirPromptSession(
1988+ mp::DisplayServer& server,
1989+ std::shared_ptr<mcl::EventHandlerRegister> const& event_handler_register) :
1990+ server(server),
1991+ event_handler_register(event_handler_register),
1992+ event_handler_register_id{event_handler_register->register_event_handler(
1993+ [this](MirEvent const& event)
1994+ {
1995+ if (event.type == mir_event_type_prompt_session_state_change)
1996+ set_state(event.prompt_session.new_state);
1997+ })},
1998+ state(mir_prompt_session_state_stopped),
1999+ handle_prompt_session_state_change{[](MirPromptSessionState){}}
2000+{
2001+}
2002+
2003+MirPromptSession::~MirPromptSession()
2004+{
2005+ set_state(mir_prompt_session_state_stopped);
2006+}
2007+
2008+void MirPromptSession::set_state(MirPromptSessionState new_state)
2009+{
2010+ std::lock_guard<decltype(event_handler_mutex)> lock(event_handler_mutex);
2011+
2012+ if (new_state != state)
2013+ {
2014+ handle_prompt_session_state_change(new_state);
2015+
2016+ if (new_state == mir_prompt_session_state_stopped)
2017+ {
2018+ event_handler_register->unregister_event_handler(event_handler_register_id);
2019+ }
2020+
2021+ state = new_state;
2022+ }
2023+}
2024+
2025+MirWaitHandle* MirPromptSession::start(pid_t application_pid, mir_prompt_session_callback callback, void* context)
2026+{
2027+ {
2028+ std::lock_guard<decltype(mutex)> lock(mutex);
2029+ parameters.set_application_pid(application_pid);
2030+ }
2031+
2032+ start_wait_handle.expect_result();
2033+ server.start_prompt_session(
2034+ 0,
2035+ &parameters,
2036+ &session,
2037+ google::protobuf::NewCallback(this, &MirPromptSession::done_start,
2038+ callback, context));
2039+
2040+ return &start_wait_handle;
2041+}
2042+
2043+MirWaitHandle* MirPromptSession::stop(mir_prompt_session_callback callback, void* context)
2044+{
2045+ stop_wait_handle.expect_result();
2046+
2047+ server.stop_prompt_session(
2048+ 0,
2049+ &protobuf_void,
2050+ &protobuf_void,
2051+ google::protobuf::NewCallback(this, &MirPromptSession::done_stop,
2052+ callback, context));
2053+
2054+ return &stop_wait_handle;
2055+}
2056+
2057+MirWaitHandle* MirPromptSession::add_prompt_provider(pid_t provider_pid,
2058+ mir_prompt_session_add_prompt_provider_callback callback,
2059+ void* context)
2060+{
2061+ {
2062+ std::lock_guard<decltype(mutex)> lock(mutex);
2063+ prompt_provider.set_pid(provider_pid);
2064+ }
2065+
2066+ add_result_wait_handle.expect_result();
2067+ server.add_prompt_provider(
2068+ 0,
2069+ &prompt_provider,
2070+ &add_result,
2071+ google::protobuf::NewCallback(this, &MirPromptSession::done_add_prompt_provider,
2072+ callback, context));
2073+
2074+ return &add_result_wait_handle;
2075+}
2076+
2077+void MirPromptSession::register_prompt_session_state_change_callback(
2078+ mir_prompt_session_state_change_callback callback,
2079+ void* context)
2080+{
2081+ std::lock_guard<decltype(event_handler_mutex)> lock(event_handler_mutex);
2082+
2083+ handle_prompt_session_state_change =
2084+ [this, callback, context](MirPromptSessionState new_state)
2085+ {
2086+ callback(this, new_state, context);
2087+ };
2088+}
2089+
2090+void MirPromptSession::done_start(mir_prompt_session_callback callback, void* context)
2091+{
2092+ {
2093+ std::lock_guard<decltype(session_mutex)> lock(session_mutex);
2094+
2095+ state = session.has_error() ? mir_prompt_session_state_stopped : mir_prompt_session_state_started;
2096+ }
2097+
2098+ callback(this, context);
2099+ start_wait_handle.result_received();
2100+}
2101+
2102+void MirPromptSession::done_stop(mir_prompt_session_callback callback, void* context)
2103+{
2104+ set_state(mir_prompt_session_state_stopped);
2105+
2106+ callback(this, context);
2107+ stop_wait_handle.result_received();
2108+}
2109+
2110+void MirPromptSession::done_add_prompt_provider(mir_prompt_session_add_prompt_provider_callback callback, void* context)
2111+{
2112+ MirBool added = mir_true;
2113+ if (add_result.has_error())
2114+ {
2115+ added = mir_false;
2116+ }
2117+ callback(this, added, context);
2118+ add_result_wait_handle.result_received();
2119+}
2120+
2121+char const* MirPromptSession::get_error_message()
2122+{
2123+ std::lock_guard<decltype(session_mutex)> lock(session_mutex);
2124+
2125+ if (!session.has_error())
2126+ session.set_error(std::string{});
2127+
2128+ return session.error().c_str();
2129+}
2130+
2131+MirWaitHandle* MirPromptSession::new_fds_for_prompt_providers(
2132+ unsigned int no_of_fds,
2133+ mir_client_fd_callback callback,
2134+ void * context)
2135+{
2136+ mir::protobuf::SocketFDRequest request;
2137+ request.set_number(no_of_fds);
2138+
2139+ fds_for_prompt_providers_wait_handle.expect_result();
2140+
2141+ server.new_fds_for_prompt_providers(
2142+ nullptr,
2143+ &request,
2144+ &socket_fd_response,
2145+ google::protobuf::NewCallback(this, &MirPromptSession::done_fds_for_prompt_providers,
2146+ callback, context));
2147+
2148+ return &fds_for_prompt_providers_wait_handle;
2149+}
2150+
2151+void MirPromptSession::done_fds_for_prompt_providers(
2152+ mir_client_fd_callback callback,
2153+ void* context)
2154+{
2155+ auto const size = socket_fd_response.fd_size();
2156+
2157+ std::vector<int> fds;
2158+ fds.reserve(size);
2159+
2160+ for (auto i = 0; i != size; ++i)
2161+ fds.push_back(socket_fd_response.fd(i));
2162+
2163+ callback(this, size, fds.data(), context);
2164+ fds_for_prompt_providers_wait_handle.result_received();
2165+}
2166
2167=== added file 'src/client/mir_prompt_session.h'
2168--- src/client/mir_prompt_session.h 1970-01-01 00:00:00 +0000
2169+++ src/client/mir_prompt_session.h 2014-06-16 15:01:17 +0000
2170@@ -0,0 +1,94 @@
2171+/*
2172+ * Copyright © 2014 Canonical Ltd.
2173+ *
2174+ * This program is free software: you can redistribute it and/or modify it
2175+ * under the terms of the GNU Lesser General Public License version 3,
2176+ * as published by the Free Software Foundation.
2177+ *
2178+ * This program is distributed in the hope that it will be useful,
2179+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2180+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2181+ * GNU Lesser General Public License for more details.
2182+ *
2183+ * You should have received a copy of the GNU Lesser General Public License
2184+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2185+ *
2186+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
2187+ */
2188+
2189+#ifndef MIR_CLIENT_MIR_PROMPT_SESSION_H_
2190+#define MIR_CLIENT_MIR_PROMPT_SESSION_H_
2191+
2192+#include "mir_toolkit/mir_client_library.h"
2193+
2194+#include "mir_protobuf.pb.h"
2195+#include "mir_wait_handle.h"
2196+
2197+#include <mutex>
2198+#include <memory>
2199+#include <atomic>
2200+
2201+namespace mir
2202+{
2203+/// The client-side library implementation namespace
2204+namespace client
2205+{
2206+class EventHandlerRegister;
2207+}
2208+}
2209+
2210+struct MirPromptSession
2211+{
2212+public:
2213+ MirPromptSession(mir::protobuf::DisplayServer& server,
2214+ std::shared_ptr<mir::client::EventHandlerRegister> const& event_handler_register);
2215+
2216+ ~MirPromptSession();
2217+
2218+ MirWaitHandle* start(pid_t application_pid, mir_prompt_session_callback callback, void* context);
2219+ MirWaitHandle* stop(mir_prompt_session_callback callback, void* context);
2220+ MirWaitHandle* add_prompt_provider(pid_t provider_pid, mir_prompt_session_add_prompt_provider_callback callback, void* context);
2221+
2222+ MirWaitHandle* new_fds_for_prompt_providers(
2223+ unsigned int no_of_fds,
2224+ mir_client_fd_callback callback,
2225+ void * context);
2226+
2227+ void register_prompt_session_state_change_callback(mir_prompt_session_state_change_callback callback, void* context);
2228+
2229+ char const* get_error_message();
2230+
2231+private:
2232+ std::mutex mutable mutex; // Protects parameters, wait_handles & results
2233+ mir::protobuf::DisplayServer& server;
2234+ mir::protobuf::PromptProvider prompt_provider;
2235+ mir::protobuf::PromptSessionParameters parameters;
2236+ mir::protobuf::Void add_result;
2237+ mir::protobuf::Void protobuf_void;
2238+ mir::protobuf::SocketFD socket_fd_response;
2239+ std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;
2240+ int const event_handler_register_id;
2241+
2242+ MirWaitHandle start_wait_handle;
2243+ MirWaitHandle stop_wait_handle;
2244+ MirWaitHandle add_result_wait_handle;
2245+ MirWaitHandle fds_for_prompt_providers_wait_handle;
2246+ std::atomic<MirPromptSessionState> state;
2247+
2248+ std::mutex mutable session_mutex; // Protects session
2249+ mir::protobuf::Void session;
2250+
2251+ std::mutex mutable event_handler_mutex; // Need another mutex for callback access to members
2252+ std::function<void(MirPromptSessionState)> handle_prompt_session_state_change;
2253+
2254+ void set_state(MirPromptSessionState new_state);
2255+ void done_start(mir_prompt_session_callback callback, void* context);
2256+ void done_stop(mir_prompt_session_callback callback, void* context);
2257+ void done_add_prompt_provider(mir_prompt_session_add_prompt_provider_callback callback, void* context);
2258+ void done_fds_for_prompt_providers(mir_client_fd_callback callback, void* context);
2259+ MirPromptSession(MirPromptSession const&) = delete;
2260+ MirPromptSession& operator=(MirPromptSession const&) = delete;
2261+};
2262+
2263+#endif /* MIR_CLIENT_MIR_PROMPT_SESSION_H_ */
2264+
2265
2266=== added file 'src/client/mir_prompt_session_api.cpp'
2267--- src/client/mir_prompt_session_api.cpp 1970-01-01 00:00:00 +0000
2268+++ src/client/mir_prompt_session_api.cpp 2014-06-16 15:01:17 +0000
2269@@ -0,0 +1,118 @@
2270+/*
2271+ * Copyright © 2014 Canonical Ltd.
2272+ *
2273+ * This program is free software: you can redistribute it and/or modify it
2274+ * under the terms of the GNU Lesser General Public License version 3,
2275+ * as published by the Free Software Foundation.
2276+ *
2277+ * This program is distributed in the hope that it will be useful,
2278+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2279+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2280+ * GNU Lesser General Public License for more details.
2281+ *
2282+ * You should have received a copy of the GNU Lesser General Public License
2283+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2284+ *
2285+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
2286+ */
2287+
2288+
2289+#include "mir_toolkit/mir_prompt_session.h"
2290+#include "mir_prompt_session.h"
2291+#include "mir_connection.h"
2292+
2293+#include <stdexcept>
2294+#include <boost/throw_exception.hpp>
2295+
2296+namespace
2297+{
2298+void null_callback(MirPromptSession*, void*) {}
2299+
2300+void add_prompt_provider_callback(
2301+ MirPromptSession*,
2302+ MirBool added,
2303+ void* context)
2304+{
2305+ if (context)
2306+ *(MirBool*)context = added;
2307+}
2308+
2309+}
2310+
2311+MirPromptSession *mir_connection_create_prompt_session_sync(
2312+ MirConnection* connection,
2313+ pid_t application_pid,
2314+ mir_prompt_session_state_change_callback state_change_callback,
2315+ void* context)
2316+{
2317+ try
2318+ {
2319+ auto prompt_session = connection->create_prompt_session();
2320+ if (state_change_callback)
2321+ prompt_session->register_prompt_session_state_change_callback(state_change_callback, context);
2322+
2323+ mir_wait_for(prompt_session->start(application_pid,
2324+ null_callback,
2325+ nullptr));
2326+ return prompt_session;
2327+ }
2328+ catch (std::exception const&)
2329+ {
2330+ // TODO callback with an error
2331+ return nullptr;
2332+ }
2333+}
2334+
2335+MirWaitHandle *mir_prompt_session_add_prompt_provider(
2336+ MirPromptSession *prompt_session,
2337+ pid_t provider_pid,
2338+ mir_prompt_session_add_prompt_provider_callback callback,
2339+ void* context)
2340+{
2341+ try
2342+ {
2343+ return prompt_session->add_prompt_provider(provider_pid, callback, context);
2344+ }
2345+ catch (std::exception const&)
2346+ {
2347+ // TODO callback with an error
2348+ return nullptr;
2349+ }
2350+}
2351+
2352+MirBool mir_prompt_session_add_prompt_provider_sync(
2353+ MirPromptSession *prompt_session,
2354+ pid_t provider_pid)
2355+{
2356+ MirBool result;
2357+ mir_wait_for(mir_prompt_session_add_prompt_provider(prompt_session,
2358+ provider_pid,
2359+ add_prompt_provider_callback,
2360+ &result));
2361+ return result;
2362+}
2363+
2364+MirWaitHandle* mir_prompt_session_new_fds_for_prompt_providers(
2365+ MirPromptSession *prompt_session,
2366+ unsigned int no_of_fds,
2367+ mir_client_fd_callback callback,
2368+ void * context)
2369+{
2370+ try
2371+ {
2372+ return prompt_session ?
2373+ prompt_session->new_fds_for_prompt_providers(no_of_fds, callback, context) :
2374+ nullptr;
2375+ }
2376+ catch (std::exception const&)
2377+ {
2378+ return nullptr;
2379+ }
2380+}
2381+
2382+void mir_prompt_session_release_sync(
2383+ MirPromptSession *prompt_session)
2384+{
2385+ mir_wait_for(prompt_session->stop(&null_callback, nullptr));
2386+ delete prompt_session;
2387+}
2388
2389=== modified file 'src/client/rpc/make_rpc_channel.h'
2390--- src/client/rpc/make_rpc_channel.h 2014-03-06 06:05:17 +0000
2391+++ src/client/rpc/make_rpc_channel.h 2014-06-16 15:01:17 +0000
2392@@ -29,6 +29,7 @@
2393 class SurfaceMap;
2394 class DisplayConfiguration;
2395 class LifecycleControl;
2396+class EventSink;
2397
2398 namespace rpc
2399 {
2400@@ -39,7 +40,8 @@
2401 std::shared_ptr<SurfaceMap> const& map,
2402 std::shared_ptr<DisplayConfiguration> const& disp_conf,
2403 std::shared_ptr<RpcReport> const& rpc_report,
2404- std::shared_ptr<LifecycleControl> const& lifecycle_control);
2405+ std::shared_ptr<LifecycleControl> const& lifecycle_control,
2406+ std::shared_ptr<EventSink> const& event_distributor);
2407 }
2408 }
2409 }
2410
2411=== modified file 'src/client/rpc/make_socket_rpc_channel.cpp'
2412--- src/client/rpc/make_socket_rpc_channel.cpp 2014-03-06 06:05:17 +0000
2413+++ src/client/rpc/make_socket_rpc_channel.cpp 2014-06-16 15:01:17 +0000
2414@@ -44,13 +44,14 @@
2415 std::shared_ptr<mcl::SurfaceMap> const& map,
2416 std::shared_ptr<mcl::DisplayConfiguration> const& disp_conf,
2417 std::shared_ptr<RpcReport> const& rpc_report,
2418- std::shared_ptr<mcl::LifecycleControl> const& lifecycle_control)
2419+ std::shared_ptr<mcl::LifecycleControl> const& lifecycle_control,
2420+ std::shared_ptr<mcl::EventSink> const& event_sink)
2421 {
2422 if (fd_prefix.is_start_of(name))
2423 {
2424 auto const fd = atoi(name.c_str()+fd_prefix.size);
2425- return std::make_shared<MirSocketRpcChannel>(fd, map, disp_conf, rpc_report, lifecycle_control);
2426+ return std::make_shared<MirSocketRpcChannel>(fd, map, disp_conf, rpc_report, lifecycle_control, event_sink);
2427 }
2428
2429- return std::make_shared<MirSocketRpcChannel>(name, map, disp_conf, rpc_report, lifecycle_control);
2430+ return std::make_shared<MirSocketRpcChannel>(name, map, disp_conf, rpc_report, lifecycle_control, event_sink);
2431 }
2432
2433=== modified file 'src/client/rpc/mir_socket_rpc_channel.cpp'
2434--- src/client/rpc/mir_socket_rpc_channel.cpp 2014-06-09 17:16:32 +0000
2435+++ src/client/rpc/mir_socket_rpc_channel.cpp 2014-06-16 15:01:17 +0000
2436@@ -24,6 +24,7 @@
2437 #include "../mir_surface.h"
2438 #include "../display_configuration.h"
2439 #include "../lifecycle_control.h"
2440+#include "../event_sink.h"
2441 #include "mir/variable_length_array.h"
2442
2443 #include "mir_protobuf.pb.h" // For Buffer frig
2444@@ -50,7 +51,8 @@
2445 std::shared_ptr<mcl::SurfaceMap> const& surface_map,
2446 std::shared_ptr<DisplayConfiguration> const& disp_config,
2447 std::shared_ptr<RpcReport> const& rpc_report,
2448- std::shared_ptr<LifecycleControl> const& lifecycle_control) :
2449+ std::shared_ptr<LifecycleControl> const& lifecycle_control,
2450+ std::shared_ptr<EventSink> const& event_sink) :
2451 rpc_report(rpc_report),
2452 pending_calls(rpc_report),
2453 work(io_service),
2454@@ -58,6 +60,7 @@
2455 surface_map(surface_map),
2456 display_configuration(disp_config),
2457 lifecycle_control(lifecycle_control),
2458+ event_sink(event_sink),
2459 disconnected(false)
2460 {
2461 socket.connect(endpoint);
2462@@ -69,7 +72,8 @@
2463 std::shared_ptr<mcl::SurfaceMap> const& surface_map,
2464 std::shared_ptr<DisplayConfiguration> const& disp_config,
2465 std::shared_ptr<RpcReport> const& rpc_report,
2466- std::shared_ptr<LifecycleControl> const& lifecycle_control) :
2467+ std::shared_ptr<LifecycleControl> const& lifecycle_control,
2468+ std::shared_ptr<EventSink> const& event_sink) :
2469 rpc_report(rpc_report),
2470 pending_calls(rpc_report),
2471 work(io_service),
2472@@ -77,6 +81,7 @@
2473 surface_map(surface_map),
2474 display_configuration(disp_config),
2475 lifecycle_control(lifecycle_control),
2476+ event_sink(event_sink),
2477 disconnected(false)
2478 {
2479 socket.assign(boost::asio::local::stream_protocol(), native_socket);
2480@@ -413,11 +418,25 @@
2481
2482 rpc_report->event_parsing_succeeded(e);
2483
2484- surface_map->with_surface_do(e.surface.id,
2485- [&e](MirSurface* surface)
2486- {
2487- surface->handle_event(e);
2488- });
2489+ event_sink->handle_event(e);
2490+
2491+ // todo - surfaces should register with the event handler register.
2492+ if (e.type == mir_event_type_surface)
2493+ {
2494+ surface_map->with_surface_do(e.surface.id,
2495+ [&e](MirSurface* surface)
2496+ {
2497+ surface->handle_event(e);
2498+ });
2499+ }
2500+ else if (e.type == mir_event_type_resize)
2501+ {
2502+ surface_map->with_surface_do(e.resize.surface_id,
2503+ [&e](MirSurface* surface)
2504+ {
2505+ surface->handle_event(e);
2506+ });
2507+ }
2508 }
2509 else
2510 {
2511
2512=== modified file 'src/client/rpc/mir_socket_rpc_channel.h'
2513--- src/client/rpc/mir_socket_rpc_channel.h 2014-06-02 17:07:02 +0000
2514+++ src/client/rpc/mir_socket_rpc_channel.h 2014-06-16 15:01:17 +0000
2515@@ -44,6 +44,7 @@
2516 class DisplayConfiguration;
2517 class SurfaceMap;
2518 class LifecycleControl;
2519+class EventSink;
2520 namespace rpc
2521 {
2522
2523@@ -56,13 +57,15 @@
2524 std::shared_ptr<SurfaceMap> const& surface_map,
2525 std::shared_ptr<DisplayConfiguration> const& disp_config,
2526 std::shared_ptr<RpcReport> const& rpc_report,
2527- std::shared_ptr<LifecycleControl> const& lifecycle_control);
2528+ std::shared_ptr<LifecycleControl> const& lifecycle_control,
2529+ std::shared_ptr<EventSink> const& event_sink);
2530
2531 MirSocketRpcChannel(int native_socket,
2532 std::shared_ptr<SurfaceMap> const& surface_map,
2533 std::shared_ptr<DisplayConfiguration> const& disp_config,
2534 std::shared_ptr<RpcReport> const& rpc_report,
2535- std::shared_ptr<LifecycleControl> const& lifecycle_control);
2536+ std::shared_ptr<LifecycleControl> const& lifecycle_control,
2537+ std::shared_ptr<EventSink> const& event_sink);
2538 ~MirSocketRpcChannel();
2539
2540 private:
2541@@ -102,6 +105,7 @@
2542 std::shared_ptr<SurfaceMap> surface_map;
2543 std::shared_ptr<DisplayConfiguration> display_configuration;
2544 std::shared_ptr<LifecycleControl> lifecycle_control;
2545+ std::shared_ptr<EventSink> event_sink;
2546 std::atomic<bool> disconnected;
2547 };
2548
2549
2550=== modified file 'src/server/default_server_configuration.cpp'
2551--- src/server/default_server_configuration.cpp 2014-06-02 17:07:02 +0000
2552+++ src/server/default_server_configuration.cpp 2014-06-16 15:01:17 +0000
2553@@ -37,6 +37,7 @@
2554 #include "mir/time/high_resolution_clock.h"
2555 #include "mir/geometry/rectangles.h"
2556 #include "mir/default_configuration.h"
2557+#include "mir/scene/null_prompt_session_listener.h"
2558
2559 #include <map>
2560 #include <vector>
2561@@ -90,6 +91,16 @@
2562 });
2563 }
2564
2565+std::shared_ptr<ms::PromptSessionListener>
2566+mir::DefaultServerConfiguration::the_prompt_session_listener()
2567+{
2568+ return prompt_session_listener(
2569+ [this]
2570+ {
2571+ return std::make_shared<ms::NullPromptSessionListener>();
2572+ });
2573+}
2574+
2575 std::shared_ptr<mi::CursorListener>
2576 mir::DefaultServerConfiguration::the_cursor_listener()
2577 {
2578
2579=== modified file 'src/server/frontend/protobuf_message_processor.cpp'
2580--- src/server/frontend/protobuf_message_processor.cpp 2014-06-02 17:07:02 +0000
2581+++ src/server/frontend/protobuf_message_processor.cpp 2014-06-16 15:01:17 +0000
2582@@ -183,9 +183,21 @@
2583 {
2584 invoke(this, display_server.get(), &protobuf::DisplayServer::configure_cursor, invocation);
2585 }
2586- else if ("new_fds_for_trusted_clients" == invocation.method_name())
2587- {
2588- invoke(this, display_server.get(), &protobuf::DisplayServer::new_fds_for_trusted_clients, invocation);
2589+ else if ("new_fds_for_prompt_providers" == invocation.method_name())
2590+ {
2591+ invoke(this, display_server.get(), &protobuf::DisplayServer::new_fds_for_prompt_providers, invocation);
2592+ }
2593+ else if ("start_prompt_session" == invocation.method_name())
2594+ {
2595+ invoke(this, display_server.get(), &protobuf::DisplayServer::start_prompt_session, invocation);
2596+ }
2597+ else if ("add_prompt_provider" == invocation.method_name())
2598+ {
2599+ invoke(this, display_server.get(), &protobuf::DisplayServer::add_prompt_provider, invocation);
2600+ }
2601+ else if ("stop_prompt_session" == invocation.method_name())
2602+ {
2603+ invoke(this, display_server.get(), &protobuf::DisplayServer::stop_prompt_session, invocation);
2604 }
2605 else if ("disconnect" == invocation.method_name())
2606 {
2607
2608=== modified file 'src/server/frontend/session_mediator.cpp'
2609--- src/server/frontend/session_mediator.cpp 2014-06-03 16:43:38 +0000
2610+++ src/server/frontend/session_mediator.cpp 2014-06-16 15:01:17 +0000
2611@@ -41,6 +41,8 @@
2612 #include "mir/frontend/client_constants.h"
2613 #include "mir/frontend/event_sink.h"
2614 #include "mir/frontend/screencast.h"
2615+#include "mir/frontend/prompt_session.h"
2616+#include "mir/scene/prompt_session_creation_parameters.h"
2617
2618 #include "mir/geometry/rectangles.h"
2619 #include "client_buffer_tracker.h"
2620@@ -423,9 +425,16 @@
2621 done->Run();
2622 }
2623
2624-std::function<void(std::shared_ptr<mf::Session> const&)> mf::SessionMediator::trusted_connect_handler() const
2625+std::function<void(std::shared_ptr<mf::Session> const&)> mf::SessionMediator::prompt_session_connect_handler() const
2626 {
2627- return [](std::shared_ptr<frontend::Session> const&) {};
2628+ return [this](std::shared_ptr<frontend::Session> const& session)
2629+ {
2630+ auto prompt_session = weak_prompt_session.lock();
2631+ if (prompt_session.get() == nullptr)
2632+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid prompt session"));
2633+
2634+ shell->add_prompt_provider_for(prompt_session, session);
2635+ };
2636 }
2637
2638 void mf::SessionMediator::configure_cursor(
2639@@ -446,7 +455,7 @@
2640
2641 auto const id = frontend::SurfaceId(cursor_request->surfaceid().value());
2642 auto const surface = session->get_surface(id);
2643-
2644+
2645 if (cursor_request->has_name())
2646 {
2647 auto const& image = cursor_images->image(cursor_request->name(), mg::default_cursor_size);
2648@@ -460,7 +469,7 @@
2649 done->Run();
2650 }
2651
2652-void mf::SessionMediator::new_fds_for_trusted_clients(
2653+void mf::SessionMediator::new_fds_for_prompt_providers(
2654 ::google::protobuf::RpcController* ,
2655 ::mir::protobuf::SocketFDRequest const* parameters,
2656 ::mir::protobuf::SocketFD* response,
2657@@ -473,8 +482,7 @@
2658 if (session.get() == nullptr)
2659 BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
2660
2661- // TODO write a handler that connects the new session to our trust session
2662- auto const connect_handler = trusted_connect_handler();
2663+ auto const connect_handler = prompt_session_connect_handler();
2664
2665 auto const fds_requested = parameters->number();
2666
2667@@ -531,6 +539,83 @@
2668 done->Run();
2669 }
2670
2671+void mf::SessionMediator::start_prompt_session(
2672+ ::google::protobuf::RpcController*,
2673+ const ::mir::protobuf::PromptSessionParameters* request,
2674+ ::mir::protobuf::Void* /*response*/,
2675+ ::google::protobuf::Closure* done)
2676+{
2677+ {
2678+ std::unique_lock<std::mutex> lock(session_mutex);
2679+ auto const session = weak_session.lock();
2680+
2681+ if (!session)
2682+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
2683+
2684+ if (weak_prompt_session.lock())
2685+ BOOST_THROW_EXCEPTION(std::runtime_error("Cannot start another prompt session"));
2686+
2687+ ms::PromptSessionCreationParameters parameters;
2688+ parameters.application_pid = request->application_pid();
2689+
2690+ report->session_start_prompt_session_called(session->name(), parameters.application_pid);
2691+
2692+ weak_prompt_session = shell->start_prompt_session_for(session, parameters);
2693+ }
2694+ done->Run();
2695+}
2696+
2697+void mf::SessionMediator::add_prompt_provider(
2698+ ::google::protobuf::RpcController*,
2699+ const ::mir::protobuf::PromptProvider* request,
2700+ ::mir::protobuf::Void*,
2701+ ::google::protobuf::Closure* done)
2702+{
2703+ {
2704+ std::unique_lock<std::mutex> lock(session_mutex);
2705+ auto const session = weak_session.lock();
2706+
2707+ if (!session)
2708+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
2709+
2710+ auto const prompt_session = weak_prompt_session.lock();
2711+
2712+ if (!prompt_session)
2713+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid prompt session"));
2714+
2715+ report->session_add_prompt_provider_called(session->name(), request->pid());
2716+ shell->add_prompt_provider_process_for(prompt_session, request->pid());
2717+ }
2718+ done->Run();
2719+}
2720+
2721+void mf::SessionMediator::stop_prompt_session(
2722+ ::google::protobuf::RpcController*,
2723+ const ::mir::protobuf::Void*,
2724+ ::mir::protobuf::Void*,
2725+ ::google::protobuf::Closure* done)
2726+{
2727+ {
2728+ std::unique_lock<std::mutex> lock(session_mutex);
2729+ auto const session = weak_session.lock();
2730+
2731+ if (!session)
2732+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
2733+
2734+ auto const prompt_session = weak_prompt_session.lock();
2735+
2736+ if (!prompt_session)
2737+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid prompt session"));
2738+
2739+ weak_prompt_session.reset();
2740+
2741+ report->session_stop_prompt_session_called(session->name());
2742+
2743+ shell->stop_prompt_session(prompt_session);
2744+ }
2745+ done->Run();
2746+}
2747+
2748 void mf::SessionMediator::pack_protobuf_buffer(
2749 protobuf::Buffer& protobuf_buffer,
2750 graphics::Buffer* graphics_buffer,
2751
2752=== modified file 'src/server/frontend/session_mediator.h'
2753--- src/server/frontend/session_mediator.h 2014-06-03 16:43:38 +0000
2754+++ src/server/frontend/session_mediator.h 2014-06-16 15:01:17 +0000
2755@@ -54,6 +54,7 @@
2756 class EventSink;
2757 class DisplayChanger;
2758 class Screencast;
2759+class PromptSession;
2760
2761 // SessionMediator relays requests from the client process into the server.
2762 class SessionMediator : public detail::DisplayServer
2763@@ -109,9 +110,9 @@
2764 google::protobuf::Closure* done) override;
2765
2766 void configure_display(::google::protobuf::RpcController* controller,
2767- const ::mir::protobuf::DisplayConfiguration* request,
2768- ::mir::protobuf::DisplayConfiguration* response,
2769- ::google::protobuf::Closure* done) override;
2770+ const ::mir::protobuf::DisplayConfiguration* request,
2771+ ::mir::protobuf::DisplayConfiguration* response,
2772+ ::google::protobuf::Closure* done) override;
2773
2774 void create_screencast(google::protobuf::RpcController*,
2775 const mir::protobuf::ScreencastParameters*,
2776@@ -127,19 +128,34 @@
2777 const mir::protobuf::ScreencastId*,
2778 mir::protobuf::Buffer*,
2779 google::protobuf::Closure* done);
2780-
2781+
2782 void configure_cursor(google::protobuf::RpcController*,
2783 mir::protobuf::CursorSetting const*,
2784 mir::protobuf::Void*,
2785 google::protobuf::Closure* done);
2786
2787+ void start_prompt_session(::google::protobuf::RpcController* controller,
2788+ const ::mir::protobuf::PromptSessionParameters* request,
2789+ ::mir::protobuf::Void* response,
2790+ ::google::protobuf::Closure* done);
2791+
2792+ void add_prompt_provider(::google::protobuf::RpcController* controller,
2793+ const ::mir::protobuf::PromptProvider* request,
2794+ ::mir::protobuf::Void*,
2795+ ::google::protobuf::Closure* done);
2796+
2797+ void stop_prompt_session(::google::protobuf::RpcController* controller,
2798+ const ::mir::protobuf::Void* request,
2799+ ::mir::protobuf::Void* response,
2800+ ::google::protobuf::Closure* done);
2801+
2802 /* Platform specific requests */
2803 void drm_auth_magic(google::protobuf::RpcController* controller,
2804 const mir::protobuf::DRMMagic* request,
2805 mir::protobuf::DRMAuthMagicStatus* response,
2806 google::protobuf::Closure* done) override;
2807
2808- void new_fds_for_trusted_clients(
2809+ void new_fds_for_prompt_providers(
2810 ::google::protobuf::RpcController* controller,
2811 ::mir::protobuf::SocketFDRequest const* parameters,
2812 ::mir::protobuf::SocketFD* response,
2813@@ -152,7 +168,7 @@
2814
2815 void advance_buffer(SurfaceId surf_id, Surface& surface, std::function<void(graphics::Buffer*, bool)> complete);
2816
2817- virtual std::function<void(std::shared_ptr<Session> const&)> trusted_connect_handler() const;
2818+ virtual std::function<void(std::shared_ptr<Session> const&)> prompt_session_connect_handler() const;
2819
2820 pid_t client_pid_;
2821 std::shared_ptr<Shell> const shell;
2822@@ -173,6 +189,7 @@
2823
2824 std::mutex session_mutex;
2825 std::weak_ptr<Session> weak_session;
2826+ std::weak_ptr<PromptSession> weak_prompt_session;
2827 };
2828
2829 }
2830
2831=== modified file 'src/server/report/logging/session_mediator_report.cpp'
2832--- src/server/report/logging/session_mediator_report.cpp 2014-05-12 21:11:05 +0000
2833+++ src/server/report/logging/session_mediator_report.cpp 2014-06-16 15:01:17 +0000
2834@@ -78,6 +78,21 @@
2835 log->log(ml::Logger::informational, "session_configure_display_called(\"" + app_name + "\")", component);
2836 }
2837
2838+void mrl::SessionMediatorReport::session_start_prompt_session_called(std::string const& app_name, pid_t application_process)
2839+{
2840+ log->log(ml::Logger::informational, "session_start_prompt_session_called(\"" + app_name + ", " + std::to_string(application_process) + ")", component);
2841+}
2842+
2843+void mrl::SessionMediatorReport::session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process)
2844+{
2845+ log->log(ml::Logger::informational, "session_add_prompt_provider_called(\"" + app_name + ", " + std::to_string(provider_process) + ")", component);
2846+}
2847+
2848+void mrl::SessionMediatorReport::session_stop_prompt_session_called(std::string const& app_name)
2849+{
2850+ log->log(ml::Logger::informational, "session_stop_prompt_session_called(\"" + app_name + "\")", component);
2851+}
2852+
2853 void mrl::SessionMediatorReport::session_error(
2854 std::string const& app_name,
2855 char const* method,
2856
2857=== modified file 'src/server/report/logging/session_mediator_report.h'
2858--- src/server/report/logging/session_mediator_report.h 2014-05-12 21:11:05 +0000
2859+++ src/server/report/logging/session_mediator_report.h 2014-06-16 15:01:17 +0000
2860@@ -59,6 +59,12 @@
2861
2862 virtual void session_configure_display_called(std::string const& app_name);
2863
2864+ virtual void session_start_prompt_session_called(std::string const& app_name, pid_t application_process);
2865+
2866+ virtual void session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process);
2867+
2868+ virtual void session_stop_prompt_session_called(std::string const& app_name);
2869+
2870 virtual void session_error(
2871 std::string const& app_name,
2872 char const* method,
2873
2874=== modified file 'src/server/report/lttng/session_mediator_report.cpp'
2875--- src/server/report/lttng/session_mediator_report.cpp 2014-05-12 21:11:05 +0000
2876+++ src/server/report/lttng/session_mediator_report.cpp 2014-06-16 15:01:17 +0000
2877@@ -39,6 +39,17 @@
2878 MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_surface_called)
2879 MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_surface_cursor_called)
2880 MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_display_called)
2881+MIR_SESSION_MEDIATOR_EVENT_METHOD(session_stop_prompt_session_called)
2882+
2883+void mir::report::lttng::SessionMediatorReport::session_start_prompt_session_called(std::string const& app_name, pid_t application_process)
2884+{
2885+ mir_tracepoint(mir_server_session_mediator, session_start_prompt_session_called, app_name.c_str(), application_process);
2886+}
2887+
2888+void mir::report::lttng::SessionMediatorReport::session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process)
2889+{
2890+ mir_tracepoint(mir_server_session_mediator, session_add_prompt_provider_called, app_name.c_str(), provider_process);
2891+}
2892
2893 void mir::report::lttng::SessionMediatorReport::session_error(std::string const& app_name, char const* method, std::string const& what)
2894 {
2895
2896=== modified file 'src/server/report/lttng/session_mediator_report.h'
2897--- src/server/report/lttng/session_mediator_report.h 2014-05-12 21:11:05 +0000
2898+++ src/server/report/lttng/session_mediator_report.h 2014-06-16 15:01:17 +0000
2899@@ -42,6 +42,9 @@
2900 void session_configure_surface_called(std::string const& app_name) override;
2901 void session_configure_surface_cursor_called(std::string const& app_name) override;
2902 void session_configure_display_called(std::string const& app_name) override;
2903+ void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) override;
2904+ void session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process) override;
2905+ void session_stop_prompt_session_called(std::string const& app_name) override;
2906
2907 void session_error(std::string const& app_name, char const* method, std::string const& what) override;
2908 private:
2909
2910=== modified file 'src/server/report/lttng/session_mediator_report_tp.h'
2911--- src/server/report/lttng/session_mediator_report_tp.h 2014-05-12 21:11:05 +0000
2912+++ src/server/report/lttng/session_mediator_report_tp.h 2014-06-16 15:01:17 +0000
2913@@ -48,6 +48,27 @@
2914 MIR_SESSION_MEDIATOR_EVENT(session_configure_surface_called)
2915 MIR_SESSION_MEDIATOR_EVENT(session_configure_surface_cursor_called)
2916 MIR_SESSION_MEDIATOR_EVENT(session_configure_display_called)
2917+MIR_SESSION_MEDIATOR_EVENT(session_stop_prompt_session_called)
2918+
2919+TRACEPOINT_EVENT(
2920+ mir_server_session_mediator,
2921+ session_start_prompt_session_called,
2922+ TP_ARGS(char const*, application, pid_t, application_process),
2923+ TP_FIELDS(
2924+ ctf_string(application, application)
2925+ ctf_integer(pid_t, application_process, application_process)
2926+ )
2927+ )
2928+
2929+TRACEPOINT_EVENT(
2930+ mir_server_session_mediator,
2931+ session_add_prompt_provider_called,
2932+ TP_ARGS(char const*, application, pid_t, provider_process),
2933+ TP_FIELDS(
2934+ ctf_string(application, application)
2935+ ctf_integer(pid_t, provider_process, provider_process)
2936+ )
2937+ )
2938
2939 TRACEPOINT_EVENT(
2940 mir_server_session_mediator,
2941
2942=== modified file 'src/server/report/null/session_mediator_report.cpp'
2943--- src/server/report/null/session_mediator_report.cpp 2014-05-12 21:11:05 +0000
2944+++ src/server/report/null/session_mediator_report.cpp 2014-06-16 15:01:17 +0000
2945@@ -54,6 +54,18 @@
2946 {
2947 }
2948
2949+void mir::report::null::SessionMediatorReport::session_start_prompt_session_called(std::string const&, pid_t)
2950+{
2951+}
2952+
2953+void mir::report::null::SessionMediatorReport::session_add_prompt_provider_called(std::string const&, pid_t)
2954+{
2955+}
2956+
2957+void mir::report::null::SessionMediatorReport::session_stop_prompt_session_called(std::string const&)
2958+{
2959+}
2960+
2961 void mir::report::null::SessionMediatorReport::session_error(
2962 std::string const&,
2963 char const* ,
2964
2965=== modified file 'src/server/report/null/session_mediator_report.h'
2966--- src/server/report/null/session_mediator_report.h 2014-05-12 21:11:05 +0000
2967+++ src/server/report/null/session_mediator_report.h 2014-06-16 15:01:17 +0000
2968@@ -53,6 +53,12 @@
2969
2970 void session_configure_display_called(std::string const& app_name) override;
2971
2972+ void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) override;
2973+
2974+ void session_add_prompt_provider_called(std::string const& app_name, pid_t provider_process) override;
2975+
2976+ void session_stop_prompt_session_called(std::string const& app_name) override;
2977+
2978 void session_error(
2979 std::string const& app_name,
2980 char const* method,
2981
2982=== modified file 'src/server/scene/CMakeLists.txt'
2983--- src/server/scene/CMakeLists.txt 2014-06-02 17:07:02 +0000
2984+++ src/server/scene/CMakeLists.txt 2014-06-16 15:01:17 +0000
2985@@ -20,4 +20,6 @@
2986 threaded_snapshot_strategy.cpp
2987 legacy_scene_change_notification.cpp
2988 legacy_surface_change_notification.cpp
2989+ prompt_session_container.cpp
2990+ prompt_session_manager_impl.cpp
2991 )
2992
2993=== modified file 'src/server/scene/application_session.cpp'
2994--- src/server/scene/application_session.cpp 2014-06-02 17:07:02 +0000
2995+++ src/server/scene/application_session.cpp 2014-06-16 15:01:17 +0000
2996@@ -23,6 +23,7 @@
2997 #include "snapshot_strategy.h"
2998 #include "mir/scene/session_listener.h"
2999 #include "mir/frontend/event_sink.h"
3000+#include "default_session_container.h"
3001
3002 #include <boost/throw_exception.hpp>
3003
3004@@ -30,6 +31,7 @@
3005 #include <memory>
3006 #include <cassert>
3007 #include <algorithm>
3008+#include <cstring>
3009
3010 namespace mf = mir::frontend;
3011 namespace ms = mir::scene;
3012@@ -181,3 +183,22 @@
3013 {
3014 event_sink->handle_lifecycle_event(state);
3015 }
3016+
3017+void ms::ApplicationSession::start_prompt_session()
3018+{
3019+ // All sessions which are part of the prompt session get this event.
3020+ MirEvent start_event;
3021+ memset(&start_event, 0, sizeof start_event);
3022+ start_event.type = mir_event_type_prompt_session_state_change;
3023+ start_event.prompt_session.new_state = mir_prompt_session_state_started;
3024+ event_sink->handle_event(start_event);
3025+}
3026+
3027+void ms::ApplicationSession::stop_prompt_session()
3028+{
3029+ MirEvent stop_event;
3030+ memset(&stop_event, 0, sizeof stop_event);
3031+ stop_event.type = mir_event_type_prompt_session_state_change;
3032+ stop_event.prompt_session.new_state = mir_prompt_session_state_stopped;
3033+ event_sink->handle_event(stop_event);
3034+}
3035
3036=== modified file 'src/server/scene/application_session.h'
3037--- src/server/scene/application_session.h 2014-06-02 17:07:02 +0000
3038+++ src/server/scene/application_session.h 2014-06-16 15:01:17 +0000
3039@@ -71,6 +71,9 @@
3040
3041 void set_lifecycle_state(MirLifecycleState state);
3042
3043+ void start_prompt_session() override;
3044+ void stop_prompt_session() override;
3045+
3046 protected:
3047 ApplicationSession(ApplicationSession const&) = delete;
3048 ApplicationSession& operator=(ApplicationSession const&) = delete;
3049
3050=== modified file 'src/server/scene/default_configuration.cpp'
3051--- src/server/scene/default_configuration.cpp 2014-06-03 16:43:38 +0000
3052+++ src/server/scene/default_configuration.cpp 2014-06-16 15:01:17 +0000
3053@@ -35,6 +35,7 @@
3054 #include "surface_controller.h"
3055 #include "surface_stack.h"
3056 #include "threaded_snapshot_strategy.h"
3057+#include "prompt_session_manager_impl.h"
3058
3059 namespace mc = mir::compositor;
3060 namespace mf = mir::frontend;
3061@@ -179,7 +180,8 @@
3062 the_shell_focus_setter(),
3063 the_snapshot_strategy(),
3064 the_session_event_sink(),
3065- the_session_listener()));
3066+ the_session_listener(),
3067+ the_prompt_session_manager()));
3068 });
3069 }
3070
3071@@ -223,3 +225,15 @@
3072 the_pixel_buffer());
3073 });
3074 }
3075+
3076+std::shared_ptr<ms::PromptSessionManager>
3077+mir::DefaultServerConfiguration::the_prompt_session_manager()
3078+{
3079+ return prompt_session_manager(
3080+ [this]()
3081+ {
3082+ return std::make_shared<ms::PromptSessionManagerImpl>(
3083+ the_session_container(),
3084+ the_prompt_session_listener());
3085+ });
3086+}
3087
3088=== added file 'src/server/scene/prompt_session_container.cpp'
3089--- src/server/scene/prompt_session_container.cpp 1970-01-01 00:00:00 +0000
3090+++ src/server/scene/prompt_session_container.cpp 2014-06-16 15:01:17 +0000
3091@@ -0,0 +1,189 @@
3092+/*
3093+ * Copyright © 2014 Canonical Ltd.
3094+ *
3095+ * This program is free software: you can redistribute it and/or modify it
3096+ * under the terms of the GNU General Public License version 3,
3097+ * as published by the Free Software Foundation.
3098+ *
3099+ * This program is distributed in the hope that it will be useful,
3100+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3101+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3102+ * GNU General Public License for more details.
3103+ *
3104+ * You should have received a copy of the GNU General Public License
3105+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3106+ *
3107+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
3108+ */
3109+
3110+#include "prompt_session_container.h"
3111+#include "mir/scene/session.h"
3112+
3113+#include <boost/throw_exception.hpp>
3114+#include <atomic>
3115+
3116+namespace ms = mir::scene;
3117+namespace mf = mir::frontend;
3118+
3119+namespace
3120+{
3121+std::atomic<unsigned> insertion_order{0};
3122+}
3123+
3124+ms::PromptSessionContainer::PromptSessionContainer()
3125+ : prompt_session_index(participant_map)
3126+ , participant_index(get<1>(participant_map))
3127+ , waiting_process_prompt_session_index(waiting_process_map)
3128+ , waiting_process_index(get<1>(waiting_process_map))
3129+{
3130+}
3131+
3132+void ms::PromptSessionContainer::insert_prompt_session(std::shared_ptr<PromptSession> const& prompt_session)
3133+{
3134+ std::unique_lock<std::mutex> lk(mutex);
3135+ prompt_sessions[prompt_session.get()] = prompt_session;
3136+}
3137+
3138+void ms::PromptSessionContainer::remove_prompt_session(std::shared_ptr<PromptSession> const& prompt_session)
3139+{
3140+ std::unique_lock<std::mutex> lk(mutex);
3141+
3142+ {
3143+ participant_by_prompt_session::iterator it, end;
3144+ boost::tie(it, end) = prompt_session_index.equal_range(prompt_session.get());
3145+ prompt_session_index.erase(it, end);
3146+ }
3147+
3148+ {
3149+ process_by_prompt_session::iterator it, end;
3150+ boost::tie(it, end) = waiting_process_prompt_session_index.equal_range(prompt_session.get());
3151+ waiting_process_prompt_session_index.erase(it, end);
3152+ }
3153+
3154+ prompt_sessions.erase(prompt_session.get());
3155+}
3156+
3157+bool ms::PromptSessionContainer::insert_participant(PromptSession* prompt_session, std::weak_ptr<Session> const& session, ParticipantType participant_type)
3158+{
3159+ std::unique_lock<std::mutex> lk(mutex);
3160+
3161+ // the prompt session must have first been added by insert_prompt_session.
3162+ if (prompt_sessions.find(prompt_session) == prompt_sessions.end())
3163+ BOOST_THROW_EXCEPTION(std::runtime_error("Prompt Session does not exist"));
3164+
3165+ if (auto locked_session = session.lock())
3166+ {
3167+ participant_by_prompt_session::iterator it;
3168+ bool valid = false;
3169+
3170+ Participant participant{prompt_session, locked_session, participant_type, insertion_order++};
3171+ boost::tie(it,valid) = participant_map.insert(participant);
3172+
3173+ if (!valid)
3174+ return false;
3175+
3176+ process_by_prompt_session::iterator process_it,end;
3177+ boost::tie(process_it,end) = waiting_process_prompt_session_index.equal_range(boost::make_tuple(prompt_session, locked_session->process_id()));
3178+ if (process_it != end)
3179+ waiting_process_prompt_session_index.erase(process_it);
3180+ return true;
3181+ }
3182+ return false;
3183+}
3184+
3185+bool ms::PromptSessionContainer::remove_participant(PromptSession* prompt_session, std::weak_ptr<Session> const& session, ParticipantType participant_type)
3186+{
3187+ std::unique_lock<std::mutex> lk(mutex);
3188+
3189+ participant_by_session::iterator it = participant_index.find(boost::make_tuple(session, participant_type, prompt_session));
3190+ if (it == participant_index.end())
3191+ return false;
3192+
3193+ participant_index.erase(it);
3194+ return true;
3195+}
3196+
3197+void ms::PromptSessionContainer::for_each_participant_in_prompt_session(
3198+ PromptSession* prompt_session,
3199+ std::function<void(std::weak_ptr<Session> const&, ms::PromptSessionContainer::ParticipantType participant_type)> f) const
3200+{
3201+ std::unique_lock<std::mutex> lk(mutex);
3202+
3203+ participant_by_prompt_session::iterator it,end;
3204+ boost::tie(it,end) = prompt_session_index.equal_range(prompt_session);
3205+
3206+ for (; it != end; ++it)
3207+ {
3208+ Participant const& participant = *it;
3209+ f(participant.session, participant.participant_type);
3210+ }
3211+}
3212+
3213+void ms::PromptSessionContainer::for_each_prompt_session_with_participant(
3214+ std::weak_ptr<Session> const& participant,
3215+ ParticipantType participant_type,
3216+ std::function<void(std::shared_ptr<PromptSession> const&)> f) const
3217+{
3218+ std::unique_lock<std::mutex> lk(mutex);
3219+
3220+ participant_by_session::iterator it,end;
3221+ boost::tie(it,end) = participant_index.equal_range(boost::make_tuple(participant, participant_type));
3222+
3223+ for (; it != end; ++it)
3224+ {
3225+ Participant const& participant = *it;
3226+
3227+ auto tsit = prompt_sessions.find(participant.prompt_session);
3228+ if (tsit != prompt_sessions.end())
3229+ f(tsit->second);
3230+ }
3231+}
3232+
3233+void ms::PromptSessionContainer::for_each_prompt_session_with_participant(
3234+ std::weak_ptr<Session> const& participant,
3235+ std::function<void(std::shared_ptr<PromptSession> const&, ParticipantType)> f) const
3236+{
3237+ std::unique_lock<std::mutex> lk(mutex);
3238+ participant_by_session::iterator it,end;
3239+ boost::tie(it,end) = participant_index.equal_range(participant);
3240+
3241+ for (; it != end; ++it)
3242+ {
3243+ Participant const& participant = *it;
3244+
3245+ auto tsit = prompt_sessions.find(participant.prompt_session);
3246+ if (tsit != prompt_sessions.end())
3247+ f(tsit->second, participant.participant_type);
3248+ }
3249+}
3250+
3251+void ms::PromptSessionContainer::insert_waiting_process(
3252+ PromptSession* prompt_session,
3253+ pid_t process_id)
3254+{
3255+ std::unique_lock<std::mutex> lk(mutex);
3256+
3257+ // the prompt session must have first been added by insert_prompt_session.
3258+ if (prompt_sessions.find(prompt_session) == prompt_sessions.end())
3259+ BOOST_THROW_EXCEPTION(std::runtime_error("Prompt Session does not exist"));
3260+
3261+ waiting_process_map.insert(WaitingProcess{prompt_session, process_id});
3262+}
3263+
3264+void ms::PromptSessionContainer::for_each_prompt_session_expecting_process(
3265+ pid_t process_id,
3266+ std::function<void(std::shared_ptr<PromptSession> const&)> f) const
3267+{
3268+ std::unique_lock<std::mutex> lk(mutex);
3269+
3270+ prompt_session_by_process::iterator it,end;
3271+ boost::tie(it,end) = waiting_process_index.equal_range(process_id);
3272+
3273+ for (; it != end; ++it)
3274+ {
3275+ WaitingProcess const& waiting_process = *it;
3276+ auto tsit = prompt_sessions.find(waiting_process.prompt_session);
3277+ if (tsit != prompt_sessions.end())
3278+ f(tsit->second);
3279+ }
3280+}
3281
3282=== added file 'src/server/scene/prompt_session_container.h'
3283--- src/server/scene/prompt_session_container.h 1970-01-01 00:00:00 +0000
3284+++ src/server/scene/prompt_session_container.h 2014-06-16 15:01:17 +0000
3285@@ -0,0 +1,158 @@
3286+/*
3287+ * Copyright © 2014 Canonical Ltd.
3288+ *
3289+ * This program is free software: you can redistribute it and/or modify it
3290+ * under the terms of the GNU General Public License version 3,
3291+ * as published by the Free Software Foundation.
3292+ *
3293+ * This program is distributed in the hope that it will be useful,
3294+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3295+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3296+ * GNU General Public License for more details.
3297+ *
3298+ * You should have received a copy of the GNU General Public License
3299+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3300+ *
3301+ * Authored By: Nick Dedekind <nick.dedekind@canonical.com>
3302+ */
3303+
3304+#ifndef MIR_SCENE_PROMPT_SESSION_CONTAINER_H_
3305+#define MIR_SCENE_PROMPT_SESSION_CONTAINER_H_
3306+
3307+#include <sys/types.h>
3308+#include <mutex>
3309+#include <unordered_map>
3310+
3311+#include <boost/multi_index_container.hpp>
3312+#include <boost/multi_index/member.hpp>
3313+#include <boost/multi_index/ordered_index.hpp>
3314+#include <boost/multi_index/composite_key.hpp>
3315+#include <boost/multi_index/mem_fun.hpp>
3316+
3317+namespace mir
3318+{
3319+using boost::multi_index_container;
3320+using namespace boost::multi_index;
3321+
3322+namespace scene
3323+{
3324+class Session;
3325+class PromptSession;
3326+
3327+class PromptSessionContainer
3328+{
3329+public:
3330+ PromptSessionContainer();
3331+ virtual ~PromptSessionContainer() = default;
3332+
3333+ enum class ParticipantType
3334+ {
3335+ helper,
3336+ application,
3337+ prompt_provider,
3338+ };
3339+
3340+ void insert_prompt_session(std::shared_ptr<PromptSession> const& prompt_session);
3341+ void remove_prompt_session(std::shared_ptr<PromptSession> const& prompt_session);
3342+
3343+ bool insert_participant(PromptSession* prompt_session, std::weak_ptr<Session> const& session, ParticipantType participant_type);
3344+ bool remove_participant(PromptSession* prompt_session, std::weak_ptr<Session> const& session, ParticipantType participant_type);
3345+
3346+ void for_each_participant_in_prompt_session(PromptSession* prompt_session, std::function<void(std::weak_ptr<Session> const&, ParticipantType)> f) const;
3347+ void for_each_prompt_session_with_participant(std::weak_ptr<Session> const& participant, ParticipantType participant_type, std::function<void(std::shared_ptr<PromptSession> const&)> f) const;
3348+ void for_each_prompt_session_with_participant(std::weak_ptr<Session> const& participant, std::function<void(std::shared_ptr<PromptSession> const&, ParticipantType)> f) const;
3349+
3350+ void insert_waiting_process(PromptSession* prompt_session, pid_t process_id);
3351+ void for_each_prompt_session_expecting_process(pid_t process_id, std::function<void(std::shared_ptr<PromptSession> const&)> f) const;
3352+
3353+private:
3354+ std::mutex mutable mutex;
3355+
3356+ std::unordered_map<PromptSession*, std::shared_ptr<PromptSession>> prompt_sessions;
3357+
3358+ struct Participant
3359+ {
3360+ PromptSession* prompt_session;
3361+ std::weak_ptr<Session> session;
3362+ ParticipantType participant_type;
3363+ uint insert_order;
3364+ };
3365+
3366+ /**
3367+ * A multi map for associating PromptSessions <-> Sessions.
3368+ * indexed by insertion order for determining the Sessions participating in a PromptSession
3369+ * and indexed for determining the Prompt Sessions in which a Session is participating.
3370+ * A Session can be associated a number of times with a single PromptSession, providing it has a different type
3371+ * eg A Session can be both a helper and a provider for a PromptSession.
3372+ */
3373+ typedef multi_index_container<
3374+ Participant,
3375+ indexed_by<
3376+ ordered_non_unique<
3377+ composite_key<
3378+ Participant,
3379+ member<Participant, PromptSession*, &Participant::prompt_session>,
3380+ member<Participant, uint, &Participant::insert_order>
3381+ >
3382+ >,
3383+ ordered_unique<
3384+ composite_key<
3385+ Participant,
3386+ member<Participant, std::weak_ptr<Session>, &Participant::session>,
3387+ member<Participant, ParticipantType, &Participant::participant_type>,
3388+ member<Participant, PromptSession*, &Participant::prompt_session>
3389+ >,
3390+ composite_key_compare<
3391+ std::owner_less<std::weak_ptr<Session>>,
3392+ std::less<ParticipantType>,
3393+ std::less<PromptSession*>>
3394+ >
3395+ >
3396+ > PromptSessionParticipants;
3397+
3398+ typedef nth_index<PromptSessionParticipants,0>::type participant_by_prompt_session;
3399+ typedef nth_index<PromptSessionParticipants,1>::type participant_by_session;
3400+
3401+ PromptSessionParticipants participant_map;
3402+ participant_by_prompt_session& prompt_session_index;
3403+ participant_by_session& participant_index;
3404+
3405+ struct WaitingProcess
3406+ {
3407+ PromptSession* prompt_session;
3408+ pid_t process_id;
3409+ };
3410+
3411+ /**
3412+ * A multimap for associating PromptSessions <-> process ids
3413+ * indexed for determining which process ids a Prompt Session is waiting for
3414+ * and the prompt sessions which are waiting for a speicific process.
3415+ */
3416+ typedef multi_index_container<
3417+ WaitingProcess,
3418+ indexed_by<
3419+ ordered_non_unique<
3420+ composite_key<
3421+ WaitingProcess,
3422+ member<WaitingProcess, PromptSession*, &WaitingProcess::prompt_session>,
3423+ member<WaitingProcess, pid_t, &WaitingProcess::process_id>
3424+ >
3425+ >,
3426+ ordered_non_unique<
3427+ member<WaitingProcess, pid_t, &WaitingProcess::process_id>
3428+ >
3429+ >
3430+ > WaitingPromptSessionsProcesses;
3431+
3432+ typedef nth_index<WaitingPromptSessionsProcesses,0>::type process_by_prompt_session;
3433+ typedef nth_index<WaitingPromptSessionsProcesses,1>::type prompt_session_by_process;
3434+
3435+ WaitingPromptSessionsProcesses waiting_process_map;
3436+ process_by_prompt_session& waiting_process_prompt_session_index;
3437+ prompt_session_by_process& waiting_process_index;
3438+};
3439+
3440+}
3441+}
3442+
3443+#endif // MIR_SCENE_PROMPT_SESSION_CONTAINER_H_
3444
3445=== added file 'src/server/scene/prompt_session_manager_impl.cpp'
3446--- src/server/scene/prompt_session_manager_impl.cpp 1970-01-01 00:00:00 +0000
3447+++ src/server/scene/prompt_session_manager_impl.cpp 2014-06-16 15:01:17 +0000
3448@@ -0,0 +1,232 @@
3449+/*
3450+ * Copyright © 2014 Canonical Ltd.
3451+ *
3452+ * This program is free software: you can redistribute it and/or modify it
3453+ * under the terms of the GNU General Public License version 3,
3454+ * as published by the Free Software Foundation.
3455+ *
3456+ * This program is distributed in the hope that it will be useful,
3457+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3458+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3459+ * GNU General Public License for more details.
3460+ *
3461+ * You should have received a copy of the GNU General Public License
3462+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3463+ *
3464+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
3465+ */
3466+
3467+#include "prompt_session_manager_impl.h"
3468+
3469+#include "mir/scene/prompt_session_creation_parameters.h"
3470+#include "mir/scene/prompt_session_listener.h"
3471+#include "mir/scene/session.h"
3472+#include "mir/scene/prompt_session.h"
3473+#include "session_container.h"
3474+#include "prompt_session_container.h"
3475+
3476+namespace ms = mir::scene;
3477+
3478+ms::PromptSessionManagerImpl::PromptSessionManagerImpl(
3479+ std::shared_ptr<SessionContainer> const& app_container,
3480+ std::shared_ptr<PromptSessionListener> const& prompt_session_listener) :
3481+ prompt_session_container(std::make_shared<PromptSessionContainer>()),
3482+ prompt_session_listener(prompt_session_listener),
3483+ app_container(app_container)
3484+{
3485+}
3486+
3487+void ms::PromptSessionManagerImpl::stop_prompt_session_locked(
3488+ std::lock_guard<std::mutex> const&,
3489+ std::shared_ptr<PromptSession> const& prompt_session) const
3490+{
3491+ std::vector<std::shared_ptr<Session>> participants;
3492+
3493+ prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(),
3494+ [&](std::weak_ptr<Session> const& session, PromptSessionContainer::ParticipantType type)
3495+ {
3496+ if (type == PromptSessionContainer::ParticipantType::helper)
3497+ {
3498+ if (auto locked_session = session.lock())
3499+ locked_session->stop_prompt_session();
3500+ }
3501+ else if (type == PromptSessionContainer::ParticipantType::prompt_provider)
3502+ {
3503+ if (auto locked_session = session.lock())
3504+ participants.push_back(locked_session);
3505+ }
3506+ });
3507+
3508+ for (auto const& participant : participants)
3509+ {
3510+ if (prompt_session_container->remove_participant(prompt_session.get(), participant, PromptSessionContainer::ParticipantType::prompt_provider))
3511+ prompt_session_listener->prompt_provider_removed(*prompt_session, participant);
3512+ }
3513+
3514+ prompt_session_container->remove_prompt_session(prompt_session);
3515+
3516+ prompt_session_listener->stopping(prompt_session);
3517+}
3518+
3519+void ms::PromptSessionManagerImpl::remove_session(std::shared_ptr<Session> const& session) const
3520+{
3521+ std::lock_guard<std::mutex> lock(prompt_sessions_mutex);
3522+
3523+ std::vector<std::pair<std::shared_ptr<PromptSession>, PromptSessionContainer::ParticipantType>> prompt_sessions;
3524+
3525+ prompt_session_container->for_each_prompt_session_with_participant(session,
3526+ [&](std::shared_ptr<PromptSession> const& prompt_session, PromptSessionContainer::ParticipantType participant_type)
3527+ {
3528+ prompt_sessions.push_back(std::make_pair(prompt_session, participant_type));
3529+ });
3530+
3531+ for(auto const& prompt_session : prompt_sessions)
3532+ {
3533+ if (prompt_session.second == PromptSessionContainer::ParticipantType::helper)
3534+ {
3535+ stop_prompt_session_locked(lock, prompt_session.first);
3536+ }
3537+ else
3538+ {
3539+ if (prompt_session_container->remove_participant(prompt_session.first.get(), session, prompt_session.second))
3540+ {
3541+ if (prompt_session.second == PromptSessionContainer::ParticipantType::prompt_provider)
3542+ prompt_session_listener->prompt_provider_removed(*prompt_session.first, session);
3543+ }
3544+ }
3545+ }
3546+}
3547+
3548+void ms::PromptSessionManagerImpl::stop_prompt_session(std::shared_ptr<PromptSession> const& prompt_session) const
3549+{
3550+ std::lock_guard<std::mutex> lock(prompt_sessions_mutex);
3551+
3552+ stop_prompt_session_locked(lock, prompt_session);
3553+}
3554+
3555+void ms::PromptSessionManagerImpl::add_prompt_provider_by_pid_locked(std::lock_guard<std::mutex> const&,
3556+ std::shared_ptr<PromptSession> const& prompt_session,
3557+ pid_t process_id) const
3558+{
3559+ prompt_session_container->insert_waiting_process(prompt_session.get(), process_id);
3560+
3561+ app_container->for_each(
3562+ [&](std::shared_ptr<Session> const& session)
3563+ {
3564+ if (session->process_id() == process_id)
3565+ {
3566+ if (prompt_session_container->insert_participant(prompt_session.get(), session, PromptSessionContainer::ParticipantType::prompt_provider))
3567+ prompt_session_listener->prompt_provider_added(*prompt_session, session);
3568+ }
3569+ });
3570+}
3571+
3572+void ms::PromptSessionManagerImpl::add_prompt_provider_by_pid(
3573+ std::shared_ptr<PromptSession> const& prompt_session,
3574+ pid_t process_id) const
3575+{
3576+ std::lock_guard<std::mutex> lock(prompt_sessions_mutex);
3577+
3578+ add_prompt_provider_by_pid_locked(lock, prompt_session, process_id);
3579+}
3580+
3581+std::shared_ptr<ms::PromptSession> ms::PromptSessionManagerImpl::start_prompt_session_for(
3582+ std::shared_ptr<Session> const& session,
3583+ PromptSessionCreationParameters const& params) const
3584+{
3585+ auto prompt_session = std::make_shared<PromptSession>();
3586+
3587+ std::lock_guard<std::mutex> lock(prompt_sessions_mutex);
3588+
3589+ prompt_session_container->insert_prompt_session(prompt_session);
3590+ if (!prompt_session_container->insert_participant(prompt_session.get(), session, PromptSessionContainer::ParticipantType::helper))
3591+ BOOST_THROW_EXCEPTION(std::runtime_error("Could not set prompt session helper"));
3592+
3593+ session->start_prompt_session();
3594+ prompt_session_listener->starting(prompt_session);
3595+
3596+ app_container->for_each(
3597+ [&](std::shared_ptr<Session> const& session)
3598+ {
3599+ if (session->process_id() == params.application_pid)
3600+ {
3601+ prompt_session_container->insert_participant(prompt_session.get(), session, PromptSessionContainer::ParticipantType::application);
3602+ }
3603+ });
3604+
3605+ return prompt_session;
3606+}
3607+
3608+void ms::PromptSessionManagerImpl::add_expected_session(std::shared_ptr<Session> const& session) const
3609+{
3610+ std::unique_lock<std::mutex> lock(prompt_sessions_mutex);
3611+
3612+ std::vector<std::shared_ptr<PromptSession>> prompt_sessions;
3613+
3614+ prompt_session_container->for_each_prompt_session_expecting_process(session->process_id(),
3615+ [&](std::shared_ptr<PromptSession> const& prompt_session)
3616+ {
3617+ prompt_sessions.push_back(prompt_session);
3618+ });
3619+
3620+ for(auto const& prompt_session : prompt_sessions)
3621+ {
3622+ if (prompt_session_container->insert_participant(prompt_session.get(), session, PromptSessionContainer::ParticipantType::prompt_provider))
3623+ prompt_session_listener->prompt_provider_added(*prompt_session, session);
3624+ }
3625+}
3626+
3627+void ms::PromptSessionManagerImpl::add_prompt_provider(
3628+ std::shared_ptr<PromptSession> const& prompt_session,
3629+ std::shared_ptr<Session> const& prompt_provider) const
3630+{
3631+ std::unique_lock<std::mutex> lock(prompt_sessions_mutex);
3632+
3633+ if (prompt_session_container->insert_participant(prompt_session.get(), prompt_provider, PromptSessionContainer::ParticipantType::prompt_provider))
3634+ prompt_session_listener->prompt_provider_added(*prompt_session, prompt_provider);
3635+}
3636+
3637+std::shared_ptr<ms::Session> ms::PromptSessionManagerImpl::application_for(
3638+ std::shared_ptr<PromptSession> const& prompt_session) const
3639+{
3640+ std::shared_ptr<Session> application_session;
3641+ std::unique_lock<std::mutex> lock(prompt_sessions_mutex);
3642+
3643+ prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(),
3644+ [&](std::weak_ptr<Session> const& session, PromptSessionContainer::ParticipantType type)
3645+ {
3646+ if (type == PromptSessionContainer::ParticipantType::application)
3647+ application_session = session.lock();
3648+ });
3649+ return application_session;
3650+}
3651+
3652+std::shared_ptr<ms::Session> ms::PromptSessionManagerImpl::helper_for(
3653+ std::shared_ptr<PromptSession> const& prompt_session) const
3654+{
3655+ std::shared_ptr<Session> helper_session;
3656+ std::unique_lock<std::mutex> lock(prompt_sessions_mutex);
3657+
3658+ prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(),
3659+ [&](std::weak_ptr<Session> const& session, PromptSessionContainer::ParticipantType type)
3660+ {
3661+ if (type == PromptSessionContainer::ParticipantType::helper)
3662+ helper_session = session.lock();
3663+ });
3664+ return helper_session;
3665+}
3666+
3667+void ms::PromptSessionManagerImpl::for_each_provider_in(
3668+ std::shared_ptr<PromptSession> const& prompt_session,
3669+ std::function<void(std::shared_ptr<Session> const& prompt_provider)> const& f) const
3670+{
3671+ std::unique_lock<std::mutex> lock(prompt_sessions_mutex);
3672+
3673+ prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(),
3674+ [&](std::weak_ptr<Session> const& session, PromptSessionContainer::ParticipantType type)
3675+ {
3676+ if (type == PromptSessionContainer::ParticipantType::prompt_provider)
3677+ if (auto locked_session = session.lock())
3678+ f(locked_session);
3679+ });
3680+}
3681
3682=== added file 'src/server/scene/prompt_session_manager_impl.h'
3683--- src/server/scene/prompt_session_manager_impl.h 1970-01-01 00:00:00 +0000
3684+++ src/server/scene/prompt_session_manager_impl.h 2014-06-16 15:01:17 +0000
3685@@ -0,0 +1,96 @@
3686+/*
3687+ * Copyright © 2014 Canonical Ltd.
3688+ *
3689+ * This program is free software: you can redistribute it and/or modify it
3690+ * under the terms of the GNU General Public License version 3,
3691+ * as published by the Free Software Foundation.
3692+ *
3693+ * This program is distributed in the hope that it will be useful,
3694+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3695+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3696+ * GNU General Public License for more details.
3697+ *
3698+ * You should have received a copy of the GNU General Public License
3699+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3700+ *
3701+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
3702+ */
3703+
3704+#ifndef MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_
3705+#define MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_
3706+
3707+#include "mir/scene/prompt_session_manager.h"
3708+#include "mir_toolkit/common.h"
3709+
3710+#include <mutex>
3711+#include <memory>
3712+
3713+namespace mir
3714+{
3715+namespace scene
3716+{
3717+class Session;
3718+class SessionContainer;
3719+class PromptSession;
3720+class PromptSessionContainer;
3721+class PromptSessionCreationParameters;
3722+class PromptSessionListener;
3723+
3724+class PromptSessionManagerImpl : public scene::PromptSessionManager
3725+{
3726+public:
3727+ explicit PromptSessionManagerImpl(
3728+ std::shared_ptr<SessionContainer> const& app_container,
3729+ std::shared_ptr<PromptSessionListener> const& prompt_session_listener);
3730+
3731+ std::shared_ptr<PromptSession> start_prompt_session_for(
3732+ std::shared_ptr<Session> const& session,
3733+ PromptSessionCreationParameters const& params) const override;
3734+
3735+ void stop_prompt_session(
3736+ std::shared_ptr<PromptSession> const& prompt_session) const override;
3737+
3738+ void add_prompt_provider(
3739+ std::shared_ptr<PromptSession> const& prompt_session,
3740+ std::shared_ptr<Session> const& prompt_provider) const override;
3741+
3742+ void add_prompt_provider_by_pid(
3743+ std::shared_ptr<PromptSession> const& prompt_session,
3744+ pid_t process_id) const override;
3745+
3746+ void add_expected_session(
3747+ std::shared_ptr<Session> const& new_session) const override;
3748+
3749+ void remove_session(
3750+ std::shared_ptr<Session> const& session) const override;
3751+
3752+ std::shared_ptr<Session> application_for(
3753+ std::shared_ptr<PromptSession> const& prompt_session) const override;
3754+
3755+ std::shared_ptr<Session> helper_for(
3756+ std::shared_ptr<PromptSession> const& prompt_session) const override;
3757+
3758+ void for_each_provider_in(
3759+ std::shared_ptr<PromptSession> const& prompt_session,
3760+ std::function<void(std::shared_ptr<Session> const& prompt_provider)> const& f) const override;
3761+
3762+private:
3763+ std::shared_ptr<PromptSessionContainer> const prompt_session_container;
3764+ std::shared_ptr<PromptSessionListener> const prompt_session_listener;
3765+ std::shared_ptr<SessionContainer> const app_container;
3766+
3767+ std::mutex mutable prompt_sessions_mutex;
3768+
3769+ void add_prompt_provider_by_pid_locked(
3770+ std::lock_guard<std::mutex> const&,
3771+ std::shared_ptr<PromptSession> const& prompt_session,
3772+ pid_t process_id) const;
3773+
3774+ void stop_prompt_session_locked(
3775+ std::lock_guard<std::mutex> const&,
3776+ std::shared_ptr<PromptSession> const& prompt_session) const;
3777+};
3778+}
3779+}
3780+
3781+#endif // MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_
3782
3783=== modified file 'src/server/scene/session_manager.cpp'
3784--- src/server/scene/session_manager.cpp 2014-04-15 05:31:19 +0000
3785+++ src/server/scene/session_manager.cpp 2014-06-16 15:01:17 +0000
3786@@ -23,6 +23,8 @@
3787 #include "mir/shell/focus_setter.h"
3788 #include "mir/scene/session.h"
3789 #include "mir/scene/session_listener.h"
3790+#include "mir/scene/prompt_session.h"
3791+#include "mir/scene/prompt_session_manager.h"
3792 #include "session_event_sink.h"
3793
3794 #include <memory>
3795@@ -38,13 +40,15 @@
3796 std::shared_ptr<msh::FocusSetter> const& focus_setter,
3797 std::shared_ptr<SnapshotStrategy> const& snapshot_strategy,
3798 std::shared_ptr<SessionEventSink> const& session_event_sink,
3799- std::shared_ptr<SessionListener> const& session_listener) :
3800+ std::shared_ptr<SessionListener> const& session_listener,
3801+ std::shared_ptr<PromptSessionManager> const& prompt_session_manager) :
3802 surface_coordinator(surface_factory),
3803 app_container(container),
3804 focus_setter(focus_setter),
3805 snapshot_strategy(snapshot_strategy),
3806 session_event_sink(session_event_sink),
3807- session_listener(session_listener)
3808+ session_listener(session_listener),
3809+ prompt_session_manager(prompt_session_manager)
3810 {
3811 assert(surface_factory);
3812 assert(container);
3813@@ -52,7 +56,7 @@
3814 assert(session_listener);
3815 }
3816
3817-ms::SessionManager::~SessionManager()
3818+ms::SessionManager::~SessionManager() noexcept
3819 {
3820 /*
3821 * Close all open sessions. We need to do this manually here
3822@@ -85,6 +89,8 @@
3823
3824 session_listener->starting(new_session);
3825
3826+ prompt_session_manager->add_expected_session(new_session);
3827+
3828 set_focus_to(new_session);
3829
3830 return new_session;
3831@@ -122,6 +128,9 @@
3832 scene_session->force_requests_to_complete();
3833
3834 session_event_sink->handle_session_stopping(scene_session);
3835+
3836+ prompt_session_manager->remove_session(scene_session);
3837+
3838 session_listener->stopping(scene_session);
3839
3840 app_container->remove_session(scene_session);
3841@@ -169,3 +178,38 @@
3842 {
3843 set_focus_to(std::dynamic_pointer_cast<Session>(session));
3844 }
3845+
3846+std::shared_ptr<mf::PromptSession> ms::SessionManager::start_prompt_session_for(std::shared_ptr<mf::Session> const& session,
3847+ PromptSessionCreationParameters const& params)
3848+{
3849+ auto shell_session = std::dynamic_pointer_cast<Session>(session);
3850+
3851+ return prompt_session_manager->start_prompt_session_for(
3852+ shell_session, params);
3853+
3854+}
3855+
3856+void ms::SessionManager::add_prompt_provider_process_for(
3857+ std::shared_ptr<mf::PromptSession> const& prompt_session,
3858+ pid_t process_id)
3859+{
3860+ auto scene_prompt_session = std::dynamic_pointer_cast<PromptSession>(prompt_session);
3861+
3862+ prompt_session_manager->add_prompt_provider_by_pid(scene_prompt_session, process_id);
3863+}
3864+
3865+void ms::SessionManager::add_prompt_provider_for(
3866+ std::shared_ptr<mf::PromptSession> const& prompt_session,
3867+ std::shared_ptr<frontend::Session> const& session)
3868+{
3869+ auto scene_prompt_session = std::dynamic_pointer_cast<PromptSession>(prompt_session);
3870+ auto scene_session = std::dynamic_pointer_cast<Session>(session);
3871+
3872+ prompt_session_manager->add_prompt_provider(scene_prompt_session, scene_session);
3873+}
3874+
3875+void ms::SessionManager::stop_prompt_session(std::shared_ptr<mf::PromptSession> const& prompt_session)
3876+{
3877+ auto scene_prompt_session = std::dynamic_pointer_cast<PromptSession>(prompt_session);
3878+ prompt_session_manager->stop_prompt_session(scene_prompt_session);
3879+}
3880
3881=== modified file 'src/server/scene/session_manager.h'
3882--- src/server/scene/session_manager.h 2014-05-23 11:18:09 +0000
3883+++ src/server/scene/session_manager.h 2014-06-16 15:01:17 +0000
3884@@ -40,6 +40,8 @@
3885 class SessionListener;
3886 class SnapshotStrategy;
3887 class SurfaceCoordinator;
3888+class PromptSessionManager;
3889+
3890
3891 class SessionManager : public SessionCoordinator
3892 {
3893@@ -49,8 +51,9 @@
3894 std::shared_ptr<shell::FocusSetter> const& focus_setter,
3895 std::shared_ptr<SnapshotStrategy> const& snapshot_strategy,
3896 std::shared_ptr<SessionEventSink> const& session_event_sink,
3897- std::shared_ptr<SessionListener> const& session_listener);
3898- virtual ~SessionManager();
3899+ std::shared_ptr<SessionListener> const& session_listener,
3900+ std::shared_ptr<PromptSessionManager> const& prompt_session_manager);
3901+ virtual ~SessionManager() noexcept;
3902
3903 virtual std::shared_ptr<frontend::Session> open_session(
3904 pid_t client_pid,
3905@@ -69,6 +72,14 @@
3906
3907 void handle_surface_created(std::shared_ptr<frontend::Session> const& session) override;
3908
3909+ std::shared_ptr<frontend::PromptSession> start_prompt_session_for(std::shared_ptr<frontend::Session> const& session,
3910+ PromptSessionCreationParameters const& params) override;
3911+ void add_prompt_provider_process_for(std::shared_ptr<frontend::PromptSession> const& prompt_session,
3912+ pid_t process_id) override;
3913+ void add_prompt_provider_for(std::shared_ptr<frontend::PromptSession> const& prompt_session,
3914+ std::shared_ptr<frontend::Session> const& session) override;
3915+ void stop_prompt_session(std::shared_ptr<frontend::PromptSession> const& prompt_session) override;
3916+
3917 protected:
3918 SessionManager(const SessionManager&) = delete;
3919 SessionManager& operator=(const SessionManager&) = delete;
3920@@ -80,6 +91,7 @@
3921 std::shared_ptr<SnapshotStrategy> const snapshot_strategy;
3922 std::shared_ptr<SessionEventSink> const session_event_sink;
3923 std::shared_ptr<SessionListener> const session_listener;
3924+ std::shared_ptr<PromptSessionManager> const prompt_session_manager;
3925
3926 std::mutex mutex;
3927 std::weak_ptr<Session> focus_application;
3928
3929=== modified file 'src/server/shell/session_coordinator_wrapper.cpp'
3930--- src/server/shell/session_coordinator_wrapper.cpp 2014-05-23 12:59:00 +0000
3931+++ src/server/shell/session_coordinator_wrapper.cpp 2014-06-16 15:01:17 +0000
3932@@ -32,7 +32,7 @@
3933 std::shared_ptr<mf::Session> msh::SessionCoordinatorWrapper::open_session(
3934 pid_t client_pid,
3935 std::string const& name,
3936- std::shared_ptr<frontend::EventSink> const& sink)
3937+ std::shared_ptr<mf::EventSink> const& sink)
3938 {
3939 return wrapped->open_session(client_pid, name, sink);
3940 }
3941@@ -67,7 +67,33 @@
3942 }
3943
3944 void msh::SessionCoordinatorWrapper::handle_surface_created(
3945- std::shared_ptr<frontend::Session> const& session)
3946+ std::shared_ptr<mf::Session> const& session)
3947 {
3948 wrapped->handle_surface_created(session);
3949 }
3950+
3951+std::shared_ptr<mf::PromptSession> msh::SessionCoordinatorWrapper::start_prompt_session_for(
3952+ std::shared_ptr<mf::Session> const& session,
3953+ scene::PromptSessionCreationParameters const& params)
3954+{
3955+ return wrapped->start_prompt_session_for(session, params);
3956+}
3957+
3958+void msh::SessionCoordinatorWrapper::add_prompt_provider_process_for(
3959+ std::shared_ptr<mf::PromptSession> const& prompt_session,
3960+ pid_t process_id)
3961+{
3962+ wrapped->add_prompt_provider_process_for(prompt_session, process_id);
3963+}
3964+
3965+void msh::SessionCoordinatorWrapper::add_prompt_provider_for(
3966+ std::shared_ptr<mf::PromptSession> const& prompt_session,
3967+ std::shared_ptr<mf::Session> const& session)
3968+{
3969+ wrapped->add_prompt_provider_for(prompt_session, session);
3970+}
3971+
3972+void msh::SessionCoordinatorWrapper::stop_prompt_session(std::shared_ptr<mf::PromptSession> const& prompt_session)
3973+{
3974+ wrapped->stop_prompt_session(prompt_session);
3975+}
3976
3977=== modified file 'src/shared/protobuf/mir_protobuf.proto'
3978--- src/shared/protobuf/mir_protobuf.proto 2014-06-02 17:07:02 +0000
3979+++ src/shared/protobuf/mir_protobuf.proto 2014-06-16 15:01:17 +0000
3980@@ -100,7 +100,7 @@
3981 optional int32 pixel_format = 4;
3982 optional int32 buffer_usage = 5;
3983 optional Buffer buffer = 6;
3984-
3985+
3986 repeated sint32 fd = 7;
3987 optional int32 fds_on_side_channel = 8;
3988
3989@@ -185,7 +185,6 @@
3990 required int32 number = 1;
3991 }
3992
3993-
3994 message SocketFD {
3995 repeated sint32 fd = 1;
3996 optional int32 fds_on_side_channel = 2;
3997@@ -193,6 +192,14 @@
3998 optional string error = 127;
3999 }
4000
4001+message PromptProvider {
4002+ required int32 pid = 1;
4003+}
4004+
4005+message PromptSessionParameters {
4006+ required int32 application_pid = 1;
4007+}
4008+
4009 service DisplayServer {
4010 // Platform independent requests
4011 rpc connect(ConnectParameters) returns (Connection);
4012@@ -212,7 +219,11 @@
4013 rpc create_screencast(ScreencastParameters) returns (Screencast);
4014 rpc screencast_buffer(ScreencastId) returns (Buffer);
4015 rpc release_screencast(ScreencastId) returns (Void);
4016-
4017+
4018 rpc configure_cursor(CursorSetting) returns (Void);
4019- rpc new_fds_for_trusted_clients(SocketFDRequest) returns (SocketFD);
4020+ rpc new_fds_for_prompt_providers(SocketFDRequest) returns (SocketFD);
4021+
4022+ rpc start_prompt_session(PromptSessionParameters) returns (Void);
4023+ rpc add_prompt_provider(PromptProvider) returns (Void);
4024+ rpc stop_prompt_session(Void) returns (Void);
4025 }
4026
4027=== modified file 'tests/acceptance-tests/CMakeLists.txt'
4028--- tests/acceptance-tests/CMakeLists.txt 2014-06-10 13:45:51 +0000
4029+++ tests/acceptance-tests/CMakeLists.txt 2014-06-16 15:01:17 +0000
4030@@ -30,11 +30,11 @@
4031 test_surfaces_with_output_id.cpp
4032 test_server_disconnect.cpp
4033 test_client_library_drm.cpp
4034+ test_prompt_session_client_api.cpp
4035 test_protobuf.cpp
4036 test_client_screencast.cpp
4037 test_client_surface_swap_buffers.cpp
4038 test_client_cursor_api.cpp
4039- test_trust_session_helper.cpp
4040 test_large_messages.cpp
4041 ${GENERATED_PROTOBUF_SRCS}
4042 ${GENERATED_PROTOBUF_HDRS}
4043
4044=== modified file 'tests/acceptance-tests/test_nested_mir.cpp'
4045--- tests/acceptance-tests/test_nested_mir.cpp 2014-05-12 21:11:05 +0000
4046+++ tests/acceptance-tests/test_nested_mir.cpp 2014-06-16 15:01:17 +0000
4047@@ -56,6 +56,9 @@
4048 MOCK_METHOD1(session_next_buffer_called, void (std::string const&));
4049 MOCK_METHOD1(session_release_surface_called, void (std::string const&));
4050 MOCK_METHOD1(session_disconnect_called, void (std::string const&));
4051+ MOCK_METHOD2(session_start_prompt_session_called, void (std::string const&, pid_t));
4052+ MOCK_METHOD2(session_add_prompt_provider_called, void (std::string const&, pid_t));
4053+ MOCK_METHOD1(session_stop_prompt_session_called, void (std::string const&));
4054
4055 void session_drm_auth_magic_called(const std::string&) override {};
4056 void session_configure_surface_called(std::string const&) override {};
4057
4058=== added file 'tests/acceptance-tests/test_prompt_session_client_api.cpp'
4059--- tests/acceptance-tests/test_prompt_session_client_api.cpp 1970-01-01 00:00:00 +0000
4060+++ tests/acceptance-tests/test_prompt_session_client_api.cpp 2014-06-16 15:01:17 +0000
4061@@ -0,0 +1,421 @@
4062+/*
4063+ * Copyright © 2014 Canonical Ltd.
4064+ *
4065+ * This program is free software: you can redistribute it and/or modify
4066+ * it under the terms of the GNU General Public License version 3 as
4067+ * published by the Free Software Foundation.
4068+ *
4069+ * This program is distributed in the hope that it will be useful,
4070+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4071+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4072+ * GNU General Public License for more details.
4073+ *
4074+ * You should have received a copy of the GNU General Public License
4075+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4076+ *
4077+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
4078+ */
4079+
4080+#include "mir_toolkit/mir_prompt_session.h"
4081+#include "mir/scene/prompt_session_listener.h"
4082+#include "mir/scene/prompt_session.h"
4083+#include "mir/scene/prompt_session_manager.h"
4084+#include "mir/scene/session.h"
4085+#include "mir/frontend/shell.h"
4086+
4087+#include "mir_test_framework/stubbed_server_configuration.h"
4088+#include "mir_test_framework/basic_client_server_fixture.h"
4089+#include "mir_test/popen.h"
4090+
4091+#include <gtest/gtest.h>
4092+#include <gmock/gmock.h>
4093+
4094+#include <condition_variable>
4095+#include <mutex>
4096+
4097+namespace mtf = mir_test_framework;
4098+namespace ms = mir::scene;
4099+namespace mf = mir::frontend;
4100+
4101+using namespace testing;
4102+
4103+namespace
4104+{
4105+struct MockPromptSessionListener : ms::PromptSessionListener
4106+{
4107+ MockPromptSessionListener(std::shared_ptr<ms::PromptSessionListener> const& wrapped) :
4108+ wrapped(wrapped)
4109+ {
4110+ ON_CALL(*this, starting(_)).WillByDefault(Invoke(wrapped.get(), &ms::PromptSessionListener::starting));
4111+ ON_CALL(*this, stopping(_)).WillByDefault(Invoke(wrapped.get(), &ms::PromptSessionListener::stopping));
4112+ ON_CALL(*this, prompt_provider_added(_, _)).WillByDefault(Invoke(wrapped.get(), &ms::PromptSessionListener::prompt_provider_added));
4113+ ON_CALL(*this, prompt_provider_removed(_, _)).WillByDefault(Invoke(wrapped.get(), &ms::PromptSessionListener::prompt_provider_removed));
4114+ }
4115+
4116+ MOCK_METHOD1(starting, void(std::shared_ptr<ms::PromptSession> const& prompt_session));
4117+ MOCK_METHOD1(stopping, void(std::shared_ptr<ms::PromptSession> const& prompt_session));
4118+
4119+ MOCK_METHOD2(prompt_provider_added, void(ms::PromptSession const& prompt_session, std::shared_ptr<ms::Session> const& prompt_provider));
4120+ MOCK_METHOD2(prompt_provider_removed, void(ms::PromptSession const& prompt_session, std::shared_ptr<ms::Session> const& prompt_provider));
4121+
4122+ std::shared_ptr<ms::PromptSessionListener> const wrapped;
4123+};
4124+
4125+struct PromptSessionListenerConfiguration : mtf::StubbedServerConfiguration
4126+{
4127+ std::shared_ptr<ms::PromptSessionListener> the_prompt_session_listener()
4128+ {
4129+ return prompt_session_listener([this]()
4130+ ->std::shared_ptr<ms::PromptSessionListener>
4131+ {
4132+ return the_mock_prompt_session_listener();
4133+ });
4134+ }
4135+
4136+ std::shared_ptr<MockPromptSessionListener> the_mock_prompt_session_listener()
4137+ {
4138+ return mock_prompt_session_listener([this]
4139+ {
4140+ return std::make_shared<NiceMock<MockPromptSessionListener>>(
4141+ mtf::StubbedServerConfiguration::the_prompt_session_listener());
4142+ });
4143+ }
4144+
4145+ mir::CachedPtr<MockPromptSessionListener> mock_prompt_session_listener;
4146+};
4147+
4148+using BasicClientServerFixture = mtf::BasicClientServerFixture<PromptSessionListenerConfiguration>;
4149+
4150+struct PromptSessionClientAPI : BasicClientServerFixture
4151+{
4152+ static constexpr mir_prompt_session_state_change_callback null_state_change_callback = nullptr;
4153+
4154+ static constexpr pid_t application_session_pid = __LINE__;
4155+ std::shared_ptr<mf::Session> application_session;
4156+
4157+ static constexpr pid_t existing_prompt_provider_pid = __LINE__;
4158+ std::shared_ptr<mf::Session> existing_prompt_provider_session;
4159+
4160+ static constexpr pid_t another_prompt_provider_pid = __LINE__;
4161+ std::shared_ptr<mf::Session> another_existing_prompt_provider;
4162+
4163+ std::shared_ptr<ms::PromptSession> server_prompt_session;
4164+
4165+ void SetUp() override
4166+ {
4167+ BasicClientServerFixture::SetUp();
4168+ application_session = server_config().the_frontend_shell()->open_session(application_session_pid, __PRETTY_FUNCTION__, std::shared_ptr<mf::EventSink>());
4169+ existing_prompt_provider_session = server_config().the_frontend_shell()->open_session(existing_prompt_provider_pid, __PRETTY_FUNCTION__, std::shared_ptr<mf::EventSink>());
4170+ another_existing_prompt_provider = server_config().the_frontend_shell()->open_session(another_prompt_provider_pid, __PRETTY_FUNCTION__, std::shared_ptr<mf::EventSink>());
4171+ }
4172+
4173+ void capture_server_prompt_session()
4174+ {
4175+ EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)).
4176+ WillOnce(DoAll(
4177+ Invoke(the_mock_prompt_session_listener()->wrapped.get(), &ms::PromptSessionListener::starting),
4178+ SaveArg<0>(&server_prompt_session)));
4179+ }
4180+
4181+ void TearDown() override
4182+ {
4183+ // TODO It really shouldn't be necessary to close these sessions.
4184+ // TODO But the MediatingDisplayChanger id destroyed without deregistering
4185+ // TODO callbacks from the BroadcastingSessionEventSink which gets called in
4186+ // TODO SessionManager::~SessionManager() in code that the comments claim
4187+ // TODO works around broken ownership.
4188+ server_config().the_frontend_shell()->close_session(another_existing_prompt_provider);
4189+ server_config().the_frontend_shell()->close_session(existing_prompt_provider_session);
4190+ server_config().the_frontend_shell()->close_session(application_session);
4191+
4192+ BasicClientServerFixture::TearDown();
4193+ }
4194+
4195+ MockPromptSessionListener* the_mock_prompt_session_listener()
4196+ {
4197+ return server_configuration.the_mock_prompt_session_listener().get();
4198+ }
4199+
4200+ MOCK_METHOD2(prompt_session_state_change, void(MirPromptSession* prompt_provider, MirPromptSessionState state));
4201+
4202+ static std::size_t const arbritary_fd_request_count = 3;
4203+ std::mutex mutex;
4204+ std::size_t actual_fd_count = 0;
4205+ int actual_fds[arbritary_fd_request_count] = {};
4206+ std::condition_variable cv;
4207+ bool called_back = false;
4208+
4209+ bool wait_for_callback(std::chrono::milliseconds timeout)
4210+ {
4211+ std::unique_lock<decltype(mutex)> lock(mutex);
4212+ return cv.wait_for(lock, timeout, [this]{ return called_back; });
4213+ }
4214+
4215+ char const* fd_connect_string(int fd)
4216+ {
4217+ static char client_connect_string[32] = {0};
4218+
4219+ sprintf(client_connect_string, "fd://%d", fd);
4220+ return client_connect_string;
4221+ }
4222+
4223+ MOCK_METHOD1(process_line, void(std::string const&));
4224+
4225+ std::vector<std::shared_ptr<mf::Session>> list_providers_for(std::shared_ptr<ms::PromptSession> const& prompt_session)
4226+ {
4227+ std::vector<std::shared_ptr<mf::Session>> results;
4228+ auto providers_fn = [&results](std::weak_ptr<ms::Session> const& session)
4229+ {
4230+ results.push_back(session.lock());
4231+ };
4232+
4233+ server_configuration.the_prompt_session_manager()->for_each_provider_in(prompt_session, providers_fn);
4234+
4235+ return results;
4236+ }
4237+};
4238+
4239+extern "C" void prompt_session_state_change_callback(MirPromptSession* prompt_provider, MirPromptSessionState state, void* context)
4240+{
4241+ PromptSessionClientAPI* self = static_cast<PromptSessionClientAPI*>(context);
4242+ self->prompt_session_state_change(prompt_provider, state);
4243+}
4244+
4245+void client_fd_callback(MirPromptSession*, size_t count, int const* fds, void* context)
4246+{
4247+ auto const self = static_cast<PromptSessionClientAPI*>(context);
4248+
4249+ std::unique_lock<decltype(self->mutex)> lock(self->mutex);
4250+ self->actual_fd_count = count;
4251+
4252+ std::copy(fds, fds+count, self->actual_fds);
4253+ self->called_back = true;
4254+ self->cv.notify_one();
4255+}
4256+
4257+struct DummyPromptProvider
4258+{
4259+ DummyPromptProvider(char const* connect_string, char const* app_name) :
4260+ connection{mir_connect_sync(connect_string, app_name)}
4261+ {
4262+ EXPECT_THAT(connection, NotNull());
4263+ }
4264+
4265+ ~DummyPromptProvider() noexcept
4266+ {
4267+ mir_connection_release(connection);
4268+ }
4269+
4270+ MirConnection* const connection;
4271+};
4272+
4273+MATCHER_P(SessionWithPid, pid, "")
4274+{
4275+ return arg->process_id() == pid;
4276+}
4277+
4278+MATCHER_P(SessionWithName, name, "")
4279+{
4280+ return arg->name() == name;
4281+}
4282+}
4283+
4284+TEST_F(PromptSessionClientAPI, can_start_and_stop_a_prompt_session)
4285+{
4286+ {
4287+ InSequence server_seq;
4288+ EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_));
4289+ EXPECT_CALL(*the_mock_prompt_session_listener(), stopping(_));
4290+ }
4291+
4292+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4293+ connection, application_session_pid, null_state_change_callback, this);
4294+ ASSERT_THAT(prompt_session, Ne(nullptr));
4295+
4296+ mir_prompt_session_release_sync(prompt_session);
4297+}
4298+
4299+TEST_F(PromptSessionClientAPI, notifies_start_and_stop)
4300+{
4301+ InSequence seq;
4302+ EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_started));
4303+ EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_stopped));
4304+
4305+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4306+ connection, application_session_pid, prompt_session_state_change_callback, this);
4307+
4308+ mir_prompt_session_release_sync(prompt_session);
4309+}
4310+
4311+TEST_F(PromptSessionClientAPI, can_add_preexisting_prompt_provider)
4312+{
4313+ {
4314+ auto const prompt_provider = std::dynamic_pointer_cast<ms::Session>(existing_prompt_provider_session);
4315+ InSequence server_seq;
4316+ EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_added(_, Eq(prompt_provider)));
4317+ EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_removed(_, Eq(prompt_provider)));
4318+ }
4319+
4320+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4321+ connection, application_session_pid, null_state_change_callback, this);
4322+
4323+ EXPECT_TRUE(mir_prompt_session_add_prompt_provider_sync(prompt_session, existing_prompt_provider_pid));
4324+
4325+ mir_prompt_session_release_sync(prompt_session);
4326+}
4327+
4328+TEST_F(PromptSessionClientAPI, can_get_fds_for_prompt_providers)
4329+{
4330+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4331+ connection, application_session_pid, null_state_change_callback, this);
4332+
4333+ mir_prompt_session_new_fds_for_prompt_providers(prompt_session, arbritary_fd_request_count, &client_fd_callback, this);
4334+ EXPECT_TRUE(wait_for_callback(std::chrono::milliseconds(500)));
4335+
4336+ EXPECT_THAT(actual_fd_count, Eq(arbritary_fd_request_count));
4337+
4338+ mir_prompt_session_release_sync(prompt_session);
4339+}
4340+
4341+TEST_F(PromptSessionClientAPI, when_prompt_provider_connects_over_fd_prompt_provider_added_with_right_pid)
4342+{
4343+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4344+ connection, application_session_pid, null_state_change_callback, this);
4345+
4346+ mir_prompt_session_new_fds_for_prompt_providers(prompt_session, 1, &client_fd_callback, this);
4347+ ASSERT_TRUE(wait_for_callback(std::chrono::milliseconds(500)));
4348+
4349+ auto const expected_pid = getpid();
4350+
4351+ EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_added(_, SessionWithPid(expected_pid)));
4352+
4353+ DummyPromptProvider{fd_connect_string(actual_fds[0]), __PRETTY_FUNCTION__};
4354+
4355+ mir_prompt_session_release_sync(prompt_session);
4356+}
4357+
4358+// TODO we need a nice way to run this (and similar tests that require a separate client process) in CI
4359+// Disabled as we can't be sure the mir_demo_client_basic is about
4360+TEST_F(PromptSessionClientAPI, DISABLED_client_pid_is_associated_with_session)
4361+{
4362+ auto const server_pid = getpid();
4363+
4364+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4365+ connection, application_session_pid, null_state_change_callback, this);
4366+
4367+ mir_prompt_session_new_fds_for_prompt_providers(prompt_session, 1, &client_fd_callback, this);
4368+ wait_for_callback(std::chrono::milliseconds(500));
4369+
4370+ EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_added(_, Not(SessionWithPid(server_pid))));
4371+
4372+ InSequence seq;
4373+ EXPECT_CALL(*this, process_line(StrEq("Starting")));
4374+ EXPECT_CALL(*this, process_line(StrEq("Connected")));
4375+ EXPECT_CALL(*this, process_line(StrEq("Surface created")));
4376+ EXPECT_CALL(*this, process_line(StrEq("Surface released")));
4377+ EXPECT_CALL(*this, process_line(StrEq("Connection released")));
4378+
4379+ mir::test::Popen output(std::string("bin/mir_demo_client_basic -m ") + fd_connect_string(actual_fds[0]));
4380+
4381+ std::string line;
4382+ while (output.get_line(line)) process_line(line);
4383+
4384+ mir_prompt_session_release_sync(prompt_session);
4385+}
4386+
4387+TEST_F(PromptSessionClientAPI, notifies_when_server_closes_prompt_session)
4388+{
4389+ EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_started));
4390+
4391+ capture_server_prompt_session();
4392+
4393+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4394+ connection, application_session_pid, prompt_session_state_change_callback, this);
4395+
4396+ EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_stopped));
4397+
4398+ server_configuration.the_prompt_session_manager()->stop_prompt_session(server_prompt_session);
4399+
4400+ // Verify we have got the "stopped" notification before we go on and release the session
4401+ Mock::VerifyAndClearExpectations(the_mock_prompt_session_listener());
4402+
4403+ mir_prompt_session_release_sync(prompt_session);
4404+}
4405+
4406+TEST_F(PromptSessionClientAPI, after_server_closes_prompt_session_api_isnt_broken)
4407+{
4408+ capture_server_prompt_session();
4409+
4410+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4411+ connection, application_session_pid, null_state_change_callback, this);
4412+
4413+ server_configuration.the_prompt_session_manager()->stop_prompt_session(server_prompt_session);
4414+
4415+ EXPECT_FALSE(mir_prompt_session_add_prompt_provider_sync(prompt_session, existing_prompt_provider_pid));
4416+
4417+ mir_prompt_session_release_sync(prompt_session);
4418+}
4419+
4420+TEST_F(PromptSessionClientAPI, server_retrieves_application_session)
4421+{
4422+ capture_server_prompt_session();
4423+
4424+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4425+ connection, application_session_pid, null_state_change_callback, this);
4426+
4427+ EXPECT_THAT(server_configuration.the_prompt_session_manager()->application_for(server_prompt_session), Eq(application_session));
4428+
4429+ mir_prompt_session_release_sync(prompt_session);
4430+}
4431+
4432+TEST_F(PromptSessionClientAPI, server_retrieves_helper_session)
4433+{
4434+ capture_server_prompt_session();
4435+
4436+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4437+ connection, application_session_pid, null_state_change_callback, this);
4438+
4439+ // can get the helper session. but it will be the current pid.
4440+ EXPECT_THAT(server_configuration.the_prompt_session_manager()->helper_for(server_prompt_session), SessionWithPid(getpid()));
4441+
4442+ mir_prompt_session_release_sync(prompt_session);
4443+}
4444+
4445+TEST_F(PromptSessionClientAPI, server_retrieves_existing_provider_sessions)
4446+{
4447+ capture_server_prompt_session();
4448+
4449+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4450+ connection, application_session_pid, null_state_change_callback, this);
4451+
4452+ mir_prompt_session_add_prompt_provider_sync(prompt_session, existing_prompt_provider_pid);
4453+ mir_prompt_session_add_prompt_provider_sync(prompt_session, another_prompt_provider_pid);
4454+
4455+ EXPECT_THAT(list_providers_for(server_prompt_session), ElementsAre(existing_prompt_provider_session, another_existing_prompt_provider));
4456+
4457+ mir_prompt_session_release_sync(prompt_session);
4458+}
4459+
4460+TEST_F(PromptSessionClientAPI, server_retrieves_child_provider_sessions)
4461+{
4462+ static int const no_of_prompt_providers = 2;
4463+ char const* const child_provider_name[no_of_prompt_providers] =
4464+ {
4465+ "child_provider0",
4466+ "child_provider1"
4467+ };
4468+
4469+ capture_server_prompt_session();
4470+
4471+ MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync(
4472+ connection, application_session_pid, null_state_change_callback, this);
4473+
4474+ mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(prompt_session, no_of_prompt_providers, &client_fd_callback, this));
4475+
4476+ DummyPromptProvider child_provider1{fd_connect_string(actual_fds[0]), child_provider_name[0]};
4477+ DummyPromptProvider child_provider2{fd_connect_string(actual_fds[1]), child_provider_name[1]};
4478+
4479+ EXPECT_THAT(list_providers_for(server_prompt_session), ElementsAre(SessionWithName(child_provider_name[0]), SessionWithName(child_provider_name[1])));
4480+
4481+ mir_prompt_session_release_sync(prompt_session);
4482+}
4483
4484=== removed file 'tests/acceptance-tests/test_trust_session_helper.cpp'
4485--- tests/acceptance-tests/test_trust_session_helper.cpp 2014-06-03 16:43:38 +0000
4486+++ tests/acceptance-tests/test_trust_session_helper.cpp 1970-01-01 00:00:00 +0000
4487@@ -1,218 +0,0 @@
4488-/*
4489- * Copyright © 2014 Canonical Ltd.
4490- *
4491- * This program is free software: you can redistribute it and/or modify it
4492- * under the terms of the GNU General Public License version 3,
4493- * as published by the Free Software Foundation.
4494- *
4495- * This program is distributed in the hope that it will be useful,
4496- * but WITHOUT ANY WARRANTY; without even the implied warranty of
4497- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4498- * GNU General Public License for more details.
4499- *
4500- * You should have received a copy of the GNU General Public License
4501- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4502- *
4503- * Authored by: Alan Griffiths <alan@octopull.co.uk>
4504- */
4505-
4506-#include "mir_toolkit/mir_client_library.h"
4507-
4508-#include "src/server/frontend/session_mediator.h"
4509-
4510-#include "mir/frontend/connection_context.h"
4511-#include "mir/scene/session.h"
4512-#include "mir/graphics/graphic_buffer_allocator.h"
4513-
4514-#include "mir_test_framework/stubbed_server_configuration.h"
4515-#include "mir_test_framework/basic_client_server_fixture.h"
4516-#include "mir_test_doubles/fake_ipc_factory.h"
4517-#include "mir_test/popen.h"
4518-
4519-#include <gtest/gtest.h>
4520-#include <gmock/gmock.h>
4521-
4522-#include <algorithm>
4523-#include <condition_variable>
4524-#include <mutex>
4525-
4526-using namespace testing;
4527-
4528-namespace
4529-{
4530-struct TrustedHelperSessionMediator : mir::frontend::SessionMediator
4531-{
4532- using mir::frontend::SessionMediator::SessionMediator;
4533-
4534- std::function<void(std::shared_ptr<mir::frontend::Session> const&)> trusted_connect_handler() const override
4535- {
4536- return [this](std::shared_ptr<mir::frontend::Session> const& session)
4537- {
4538- auto const ss = std::dynamic_pointer_cast<mir::scene::Session>(session);
4539- client_pid = ss->process_id();
4540- };
4541- }
4542-
4543- pid_t mutable client_pid = 0;
4544-};
4545-
4546-std::shared_ptr<TrustedHelperSessionMediator> trusted_helper_mediator;
4547-
4548-struct FakeIpcFactory : mir::test::doubles::FakeIpcFactory
4549-{
4550- using mir::test::doubles::FakeIpcFactory::FakeIpcFactory;
4551-
4552- std::shared_ptr<mir::frontend::detail::DisplayServer> make_helper_mediator(
4553- std::shared_ptr<mir::frontend::Shell> const& shell,
4554- std::shared_ptr<mir::graphics::Platform> const& graphics_platform,
4555- std::shared_ptr<mir::frontend::DisplayChanger> const& changer,
4556- std::shared_ptr<mir::graphics::GraphicBufferAllocator> const& buffer_allocator,
4557- std::shared_ptr<mir::frontend::SessionMediatorReport> const& sm_report,
4558- std::shared_ptr<mir::frontend::EventSink> const& sink,
4559- std::shared_ptr<mir::frontend::Screencast> const& effective_screencast,
4560- mir::frontend::ConnectionContext const& connection_context,
4561- std::shared_ptr<mir::graphics::CursorImages> const& cursor_images)
4562- {
4563- trusted_helper_mediator = std::make_shared<TrustedHelperSessionMediator>(
4564- shell,
4565- graphics_platform,
4566- changer,
4567- buffer_allocator->supported_pixel_formats(),
4568- sm_report,
4569- sink,
4570- resource_cache(),
4571- effective_screencast,
4572- connection_context,
4573- cursor_images);
4574-
4575- return trusted_helper_mediator;
4576- }
4577-};
4578-
4579-struct MyServerConfiguration : mir_test_framework::StubbedServerConfiguration
4580-{
4581- std::shared_ptr<FakeIpcFactory> mediator_factory;
4582-
4583- auto the_ipc_factory(
4584- std::shared_ptr<mir::frontend::Shell> const& shell,
4585- std::shared_ptr<mir::graphics::GraphicBufferAllocator> const& allocator)
4586- -> std::shared_ptr<mir::frontend::ProtobufIpcFactory> override
4587- {
4588- return ipc_factory(
4589- [&]()
4590- {
4591- mediator_factory = std::make_shared<FakeIpcFactory>(
4592- shell,
4593- the_session_mediator_report(),
4594- the_graphics_platform(),
4595- the_frontend_display_changer(),
4596- allocator,
4597- the_screencast(),
4598- the_session_authorizer(),
4599- the_cursor_images());
4600-
4601- EXPECT_CALL(*mediator_factory,
4602- make_mediator(_, _, _, _, _, _, _, _, _)).Times(AnyNumber())
4603- .WillOnce(Invoke(mediator_factory.get(), &FakeIpcFactory::make_helper_mediator))
4604- .WillRepeatedly(Invoke(mediator_factory.get(), &FakeIpcFactory::make_default_mediator));
4605-
4606- return mediator_factory;
4607- });
4608- }
4609-};
4610-
4611-using MyBasicClientServerFixture = mir_test_framework::BasicClientServerFixture<MyServerConfiguration>;
4612-
4613-struct TrustSessionHelper : MyBasicClientServerFixture
4614-{
4615-
4616- void TearDown()
4617- {
4618- trusted_helper_mediator.reset();
4619- MyBasicClientServerFixture::TearDown();
4620- }
4621-
4622- static std::size_t const arbritary_fd_request_count = 3;
4623-
4624- std::mutex mutex;
4625- std::size_t actual_fd_count = 0;
4626- int actual_fds[arbritary_fd_request_count] = {};
4627- std::condition_variable cv;
4628- bool called_back = false;
4629-
4630- bool wait_for_callback(std::chrono::milliseconds timeout)
4631- {
4632- std::unique_lock<decltype(mutex)> lock(mutex);
4633- return cv.wait_for(lock, timeout, [this]{ return called_back; });
4634- }
4635-
4636- char client_connect_string[128] = {0};
4637-
4638- char const* fd_connect_string(int fd)
4639- {
4640- sprintf(client_connect_string, "fd://%d", fd);
4641- return client_connect_string;
4642- }
4643-
4644- MOCK_METHOD1(process_line, void(std::string const&));
4645-};
4646-
4647-void client_fd_callback(MirConnection*, size_t count, int const* fds, void* context)
4648-{
4649- auto const self = static_cast<TrustSessionHelper*>(context);
4650-
4651- std::unique_lock<decltype(self->mutex)> lock(self->mutex);
4652- self->actual_fd_count = count;
4653-
4654- std::copy(fds, fds+count, self->actual_fds);
4655- self->called_back = true;
4656- self->cv.notify_one();
4657-}
4658-}
4659-
4660-TEST_F(TrustSessionHelper, can_get_fds_for_trusted_clients)
4661-{
4662- mir_connection_new_fds_for_trusted_clients(connection, arbritary_fd_request_count, &client_fd_callback, this);
4663- EXPECT_TRUE(wait_for_callback(std::chrono::milliseconds(500)));
4664-
4665- EXPECT_THAT(actual_fd_count, Eq(arbritary_fd_request_count));
4666-}
4667-
4668-TEST_F(TrustSessionHelper, gets_pid_when_trusted_client_connects_over_fd)
4669-{
4670- mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this);
4671- wait_for_callback(std::chrono::milliseconds(500));
4672-
4673- auto const expected_pid = getpid();
4674-
4675- EXPECT_THAT(trusted_helper_mediator->client_pid, Eq(0)); // Before the client connects we don't have a pid
4676- auto client_connection = mir_connect_sync(fd_connect_string(actual_fds[0]), __PRETTY_FUNCTION__);
4677- EXPECT_THAT(trusted_helper_mediator->client_pid, Eq(expected_pid)); // After the client connects we do have a pid
4678-
4679- mir_connection_release(client_connection);
4680-}
4681-
4682-// TODO we need a nice way to run this (and similar tests that require a separate client process) in CI
4683-// Disabled as we can't be sure the mir_demo_client_basic is about
4684-TEST_F(TrustSessionHelper, DISABLED_client_pid_is_associated_with_session)
4685-{
4686- auto const server_pid = getpid();
4687-
4688- mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this);
4689- wait_for_callback(std::chrono::milliseconds(500));
4690-
4691- InSequence seq;
4692- EXPECT_CALL(*this, process_line(StrEq("Starting")));
4693- EXPECT_CALL(*this, process_line(StrEq("Connected")));
4694- EXPECT_CALL(*this, process_line(StrEq("Surface created")));
4695- EXPECT_CALL(*this, process_line(StrEq("Surface released")));
4696- EXPECT_CALL(*this, process_line(StrEq("Connection released")));
4697-
4698- mir::test::Popen output(std::string("bin/mir_demo_client_basic -m ") + fd_connect_string(actual_fds[0]));
4699-
4700- std::string line;
4701- while (output.get_line(line)) process_line(line);
4702-
4703- EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(0));
4704- EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(server_pid));
4705-}
4706
4707=== modified file 'tests/integration-tests/frontend/test_application_mediator_report.cpp'
4708--- tests/integration-tests/frontend/test_application_mediator_report.cpp 2014-05-12 21:11:05 +0000
4709+++ tests/integration-tests/frontend/test_application_mediator_report.cpp 2014-06-16 15:01:17 +0000
4710@@ -50,6 +50,12 @@
4711
4712 EXPECT_CALL(*this, session_disconnect_called(testing::_)).
4713 Times(testing::AtLeast(0));
4714+
4715+ EXPECT_CALL(*this, session_start_prompt_session_called(testing::_, testing::_)).
4716+ Times(testing::AtLeast(0));
4717+
4718+ EXPECT_CALL(*this, session_stop_prompt_session_called(testing::_)).
4719+ Times(testing::AtLeast(0));
4720 }
4721
4722 MOCK_METHOD1(session_connect_called, void (std::string const&));
4723@@ -57,6 +63,9 @@
4724 MOCK_METHOD1(session_next_buffer_called, void (std::string const&));
4725 MOCK_METHOD1(session_release_surface_called, void (std::string const&));
4726 MOCK_METHOD1(session_disconnect_called, void (std::string const&));
4727+ MOCK_METHOD2(session_start_prompt_session_called, void (std::string const&, pid_t));
4728+ MOCK_METHOD2(session_add_prompt_provider_called, void (std::string const&, pid_t));
4729+ MOCK_METHOD1(session_stop_prompt_session_called, void (std::string const&));
4730
4731 void session_drm_auth_magic_called(const std::string&) override {};
4732 void session_configure_surface_called(std::string const&) override {};
4733@@ -359,3 +368,175 @@
4734
4735 launch_client_process(client_process);
4736 }
4737+
4738+TEST_F(ApplicationMediatorReport, prompt_session_start_called)
4739+{
4740+ struct Server : TestingServerConfiguration
4741+ {
4742+ std::shared_ptr<mf::SessionMediatorReport>
4743+ the_application_mediator_report()
4744+ {
4745+ auto result = std::make_shared<MockApplicationMediatorReport>();
4746+
4747+ EXPECT_CALL(*result, session_start_prompt_session_called(testing::_, testing::_)).
4748+ Times(1);
4749+
4750+ return result;
4751+ }
4752+ } server_processing;
4753+
4754+ launch_server_process(server_processing);
4755+
4756+ struct Client: TestingClientConfiguration
4757+ {
4758+ void exec()
4759+ {
4760+ mt::TestProtobufClient client(mtf::test_socket_file(), rpc_timeout_ms);
4761+
4762+ client.connect_parameters.set_application_name(__PRETTY_FUNCTION__);
4763+ EXPECT_CALL(client, connect_done()).
4764+ Times(testing::AtLeast(0));
4765+ EXPECT_CALL(client, prompt_session_start_done()).
4766+ Times(testing::AtLeast(0));
4767+
4768+ client.display_server.connect(
4769+ 0,
4770+ &client.connect_parameters,
4771+ &client.connection,
4772+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::connect_done));
4773+
4774+ client.wait_for_connect_done();
4775+
4776+ client.display_server.start_prompt_session(
4777+ 0,
4778+ &client.prompt_session_parameters,
4779+ &client.prompt_session,
4780+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::prompt_session_start_done));
4781+ client.wait_for_prompt_session_start_done();
4782+
4783+ }
4784+ } client_process;
4785+
4786+ launch_client_process(client_process);
4787+}
4788+
4789+TEST_F(ApplicationMediatorReport, prompt_session_add_prompt_provider_called)
4790+{
4791+ struct Server : TestingServerConfiguration
4792+ {
4793+ std::shared_ptr<mf::SessionMediatorReport>
4794+ the_application_mediator_report()
4795+ {
4796+ auto result = std::make_shared<MockApplicationMediatorReport>();
4797+
4798+ EXPECT_CALL(*result, session_start_prompt_session_called(testing::_, testing::_)).
4799+ Times(1);
4800+
4801+ return result;
4802+ }
4803+ } server_processing;
4804+
4805+ launch_server_process(server_processing);
4806+
4807+ struct Client: TestingClientConfiguration
4808+ {
4809+ void exec()
4810+ {
4811+ mt::TestProtobufClient client(mtf::test_socket_file(), rpc_timeout_ms);
4812+
4813+ client.connect_parameters.set_application_name(__PRETTY_FUNCTION__);
4814+ EXPECT_CALL(client, connect_done()).
4815+ Times(testing::AtLeast(0));
4816+ EXPECT_CALL(client, prompt_session_start_done()).
4817+ Times(testing::AtLeast(0));
4818+ EXPECT_CALL(client, prompt_session_add_prompt_provider_done()).
4819+ Times(testing::AtLeast(0));
4820+
4821+ client.display_server.connect(
4822+ 0,
4823+ &client.connect_parameters,
4824+ &client.connection,
4825+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::connect_done));
4826+
4827+ client.wait_for_connect_done();
4828+
4829+ client.display_server.start_prompt_session(
4830+ 0,
4831+ &client.prompt_session_parameters,
4832+ &client.prompt_session,
4833+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::prompt_session_start_done));
4834+ client.wait_for_prompt_session_start_done();
4835+
4836+ client.display_server.add_prompt_provider(
4837+ 0,
4838+ &client.prompt_provider,
4839+ &client.ignored,
4840+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::prompt_session_add_prompt_provider_done));
4841+ client.wait_for_prompt_session_add_prompt_provider_done();
4842+
4843+ }
4844+ } client_process;
4845+
4846+ launch_client_process(client_process);
4847+}
4848+
4849+TEST_F(ApplicationMediatorReport, prompt_session_stop_called)
4850+{
4851+ struct Server : TestingServerConfiguration
4852+ {
4853+ std::shared_ptr<mf::SessionMediatorReport>
4854+ the_application_mediator_report()
4855+ {
4856+ auto result = std::make_shared<MockApplicationMediatorReport>();
4857+
4858+ EXPECT_CALL(*result, session_stop_prompt_session_called(testing::_)).
4859+ Times(1);
4860+
4861+ return result;
4862+ }
4863+ } server_processing;
4864+
4865+ launch_server_process(server_processing);
4866+
4867+ struct Client: TestingClientConfiguration
4868+ {
4869+ void exec()
4870+ {
4871+ mt::TestProtobufClient client(mtf::test_socket_file(), rpc_timeout_ms);
4872+
4873+ client.connect_parameters.set_application_name(__PRETTY_FUNCTION__);
4874+ EXPECT_CALL(client, connect_done()).
4875+ Times(testing::AtLeast(0));
4876+ EXPECT_CALL(client, prompt_session_start_done()).
4877+ Times(testing::AtLeast(0));
4878+ EXPECT_CALL(client, prompt_session_stop_done()).
4879+ Times(testing::AtLeast(0));
4880+
4881+ client.display_server.connect(
4882+ 0,
4883+ &client.connect_parameters,
4884+ &client.connection,
4885+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::connect_done));
4886+
4887+ client.wait_for_connect_done();
4888+
4889+ client.display_server.start_prompt_session(
4890+ 0,
4891+ &client.prompt_session_parameters,
4892+ &client.prompt_session,
4893+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::prompt_session_start_done));
4894+ client.wait_for_prompt_session_start_done();
4895+
4896+ client.display_server.stop_prompt_session(
4897+ 0,
4898+ &client.ignored,
4899+ &client.ignored,
4900+ google::protobuf::NewCallback(&client, &mt::TestProtobufClient::prompt_session_stop_done));
4901+ client.wait_for_prompt_session_stop_done();
4902+ }
4903+ } client_process;
4904+
4905+ launch_client_process(client_process);
4906+}
4907+
4908+
4909
4910=== modified file 'tests/integration-tests/test_session_manager.cpp'
4911--- tests/integration-tests/test_session_manager.cpp 2014-04-15 05:31:19 +0000
4912+++ tests/integration-tests/test_session_manager.cpp 2014-06-16 15:01:17 +0000
4913@@ -32,6 +32,7 @@
4914 #include "mir_test_doubles/null_snapshot_strategy.h"
4915 #include "mir_test_doubles/null_event_sink.h"
4916 #include "mir_test_doubles/null_session_event_sink.h"
4917+#include "mir_test_doubles/null_prompt_session_manager.h"
4918
4919 #include <gmock/gmock.h>
4920 #include <gtest/gtest.h>
4921@@ -55,7 +56,8 @@
4922 mt::fake_shared(focus_setter),
4923 std::make_shared<mtd::NullSnapshotStrategy>(),
4924 std::make_shared<mtd::NullSessionEventSink>(),
4925- mt::fake_shared(session_listener))
4926+ mt::fake_shared(session_listener),
4927+ std::make_shared<mtd::NullPromptSessionManager>())
4928 {
4929
4930 }
4931
4932=== modified file 'tests/mir_test_doubles/test_protobuf_client.cpp'
4933--- tests/mir_test_doubles/test_protobuf_client.cpp 2014-03-06 06:05:17 +0000
4934+++ tests/mir_test_doubles/test_protobuf_client.cpp 2014-06-16 15:01:17 +0000
4935@@ -18,6 +18,7 @@
4936
4937 #include "mir_test/test_protobuf_client.h"
4938 #include "mir_test_doubles/mock_rpc_report.h"
4939+#include "mir_test_doubles/null_client_event_sink.h"
4940
4941 #include "src/client/connection_surface_map.h"
4942 #include "src/client/display_configuration.h"
4943@@ -38,7 +39,8 @@
4944 std::make_shared<mir::client::ConnectionSurfaceMap>(),
4945 std::make_shared<mir::client::DisplayConfiguration>(),
4946 rpc_report,
4947- std::make_shared<mir::client::LifecycleControl>())),
4948+ std::make_shared<mir::client::LifecycleControl>(),
4949+ std::make_shared<mtd::NullClientEventSink>())),
4950 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
4951 maxwait(timeout_ms),
4952 connect_done_called(false),
4953@@ -59,6 +61,9 @@
4954 surface_parameters.set_buffer_usage(0);
4955 surface_parameters.set_output_id(mir_display_output_id_invalid);
4956
4957+ prompt_provider.set_pid(__LINE__);
4958+ prompt_session_parameters.set_application_pid(__LINE__);
4959+
4960 ON_CALL(*this, connect_done())
4961 .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_connect_done));
4962 ON_CALL(*this, create_surface_done())
4963@@ -73,6 +78,12 @@
4964 .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_drm_auth_magic_done));
4965 ON_CALL(*this, display_configure_done())
4966 .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_configure_display_done));
4967+ ON_CALL(*this, prompt_session_start_done())
4968+ .WillByDefault(testing::Invoke(&wc_prompt_session_start, &WaitCondition::wake_up_everyone));
4969+ ON_CALL(*this, prompt_session_add_prompt_provider_done())
4970+ .WillByDefault(testing::Invoke(&wc_prompt_session_add, &WaitCondition::wake_up_everyone));
4971+ ON_CALL(*this, prompt_session_stop_done())
4972+ .WillByDefault(testing::Invoke(&wc_prompt_session_stop, &WaitCondition::wake_up_everyone));
4973 }
4974
4975 void mir::test::TestProtobufClient::on_connect_done()
4976@@ -225,3 +236,18 @@
4977 }
4978 tfd_done_called.store(false);
4979 }
4980+
4981+void mir::test::TestProtobufClient::wait_for_prompt_session_start_done()
4982+{
4983+ wc_prompt_session_start.wait_for_at_most_seconds(maxwait);
4984+}
4985+
4986+void mir::test::TestProtobufClient::wait_for_prompt_session_add_prompt_provider_done()
4987+{
4988+ wc_prompt_session_add.wait_for_at_most_seconds(maxwait);
4989+}
4990+
4991+void mir::test::TestProtobufClient::wait_for_prompt_session_stop_done()
4992+{
4993+ wc_prompt_session_stop.wait_for_at_most_seconds(maxwait);
4994+}
4995
4996=== modified file 'tests/unit-tests/client/CMakeLists.txt'
4997--- tests/unit-tests/client/CMakeLists.txt 2014-03-06 06:05:17 +0000
4998+++ tests/unit-tests/client/CMakeLists.txt 2014-06-16 15:01:17 +0000
4999@@ -8,6 +8,8 @@
5000 ${CMAKE_CURRENT_SOURCE_DIR}/test_wait_handle.cpp
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches