Merge lp:~raof/mir/persistent-surface-id into lp:mir
- persistent-surface-id
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alberto Aguirre |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2645 |
Proposed branch: | lp:~raof/mir/persistent-surface-id |
Merge into: | lp:mir |
Diff against target: |
1234 lines (+816/-3) 31 files modified
CMakeLists.txt (+1/-0) debian/control (+1/-0) include/client/mir_toolkit/client_types.h (+5/-0) include/client/mir_toolkit/mir_surface.h (+40/-0) include/server/mir/shell/persistent_surface_store.h (+131/-0) src/client/mir_surface.cpp (+46/-0) src/client/mir_surface.h (+16/-0) src/client/mir_surface_api.cpp (+37/-0) src/client/symbols.map (+4/-0) src/include/server/mir/default_server_configuration.h (+3/-0) src/include/server/mir/frontend/shell.h (+2/-0) src/protobuf/mir_protobuf.proto (+9/-0) src/server/CMakeLists.txt (+2/-0) src/server/frontend/protobuf_message_processor.cpp (+4/-0) src/server/frontend/session_mediator.cpp (+18/-0) src/server/frontend/session_mediator.h (+6/-0) src/server/frontend/shell_wrapper.cpp (+5/-0) src/server/frontend/shell_wrapper.h (+2/-0) src/server/shell/CMakeLists.txt (+2/-0) src/server/shell/default_configuration.cpp (+12/-1) src/server/shell/default_persistent_surface_store.cpp (+91/-0) src/server/shell/default_persistent_surface_store.h (+44/-0) src/server/shell/frontend_shell.cpp (+9/-0) src/server/shell/frontend_shell.h (+10/-2) src/server/shell/persistent_surface_store.cpp (+79/-0) src/server/symbols.map (+1/-0) tests/acceptance-tests/test_client_library.cpp (+20/-0) tests/include/mir_test_doubles/mock_shell.h (+2/-0) tests/unit-tests/shell/CMakeLists.txt (+2/-0) tests/unit-tests/shell/test_default_persistent_surface_store.cpp (+124/-0) tests/unit-tests/shell/test_persistent_surface_store_id.cpp (+88/-0) |
To merge this branch: | bzr merge lp:~raof/mir/persistent-surface-id |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Alberto Aguirre (community) | Approve | ||
Alan Griffiths | Approve | ||
Daniel van Vugt | Abstain | ||
Kevin DuBois (community) | Approve | ||
Andreas Pokorny (community) | Approve | ||
Robert Carr (community) | Approve | ||
Review via email: mp+257843@code.launchpad.net |
Commit message
Add mir_surface_
This requests an ID for a surface which can be referenced by other clients and is stable over time.
API for actually using these IDs will come as a followup.
Description of the change
First - slightly useless - half of persistent surface reference support.
Adds the ability for a client to get a MirSurfaceId, which *currently* does nothing, but in a follow-up will be convertible to a string, sent cross-process, and deserialised into a MirSurfaceId that can be set in an input method spec to do cross-process surface-relative positioning.
It will also grow into support for restoring surface position/state across application restarts.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2506
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
(4) Consider using the client executable binary path instead of UUID:
readlink(
That would give a sufficiently unique string with the added bonus of completely solving issue (2) as no apps or toolkits would need modification at all. All clients would support it for free.
Chris Halse Rogers (raof) wrote : | # |
(1) If you have multiple instances of a client running, their surfaces get different IDs.
(2) If an app doesn't need to do anything that requires an ID it doesn't get one. Simple apps won't do anything requiring an ID. We punt on trying to restore surface state for apps that don't explicitly opt-in (this actually isn't any different from X).
IM frameworks are an interesting case. For native Mir clients, it's fine - the IM framework has an in-process plugin that can request the surface ID. For XMir clients we need to do something special anyway - the IM framework doesn't have any useful knowlege in that case. I was thinking of having XMir ask for a ID for all its surfaces, then setting a _X_CANONICAL_MIR_ID property that the IM framework can read out.
We'll have to do something like that regardless of how we identify surfaces (at least for sensible ways of identifying surfaces), unless we integrate XMir more closely into Mir - at which point XID → MirSurface lookups become possible, and this still works.
(3) We can guarantee this MirSurfaceId is unique. I don't want to replace our internal integer IDs with it; they serve different purposes. But we don't expose those integer IDs to the client, so I think MirSurfaceId is a reasonable name for it, client-side. I'm happy to change it, although it really is a client-side ID, so I might wait until others weigh in.
(4) /proc/self/exe is one of many bad options. For example, all Java applications have the same /proc/self/exe (that of the JVM binary). This applies to Mono, Python, shell scripts, …
Robert Carr (robertcarr) wrote : | # |
Looks basically ok to me though client API requires docs.
I have some questions about how we are going to implement persistence across shell restarts and if the client API is really sufficient though, lets talk on IRC perhaps?
Robert Carr (robertcarr) wrote : | # |
Understand how the persistence model will work now:
16:32 < RAOF> racarr: Yo!
16:34 < racarr> YO
16:34 < RAOF> You wanted to talk about persistence across shell restarts and such?
16:35 < racarr> RAOF: Ah yes...uh...so how is that going to work? Wont the API have to be like
16:35 < racarr> well involve the client having to provide some sort of cookie
16:35 < racarr> otherwise yeah how to differentiate between multiple java apps etc
16:35 < RAOF> Yeah, this would be more obvious if I'd actually documented the client API, wouldn't it :)
16:36 < RAOF> And also not included bits that should be in a subsequent MP.
16:36 < racarr> ?
16:36 < RAOF> Well, for example, mir_surface_
16:37 < RAOF> The reason being that I removed the acceptance tests which used them in order to do the minimal *get* an ID thing.
16:38 < RAOF> Anyway; my high-level idea of this is that any client which wishes to restore its surface location/state across starts needs to
first acquire an ID for the relevant surface and save it's serialised representation somewhere.
16:39 < RAOF> Then, when it's next starting up, before it creates the relevant window it deserialises the ID it got and adds the ID to the
16:39 < RAOF> If there's currently no surface with that ID active, the PersistentSurfa
16:40 < racarr> oh ok you add the ID to the surface spec
16:40 < RAOF> 43I+bool mir_surface_
16:40 < RAOF> :)
16:40 < RAOF> It's not hooked up to anything, but it's (accidentally!) there :)
16:40 < racarr> yeah I guess I had a different mental model so I
16:40 < racarr> filtered that out
Daniel van Vugt (vanvugt) wrote : | # |
Already confused by whether you mean internal integer ID by "ID" or MirSurfaceId. Please fix (3) ASAP. Having two broadly unrelated meanings of "surface ID" is going to keep biting us.
(1) What I mean is consider if you have only one app installed and do this:
* Open one instance A. Close instance A.
* Open two instances B and C. Close instances B and C.
* Open one instance D.
Now exactly which instances have the same MirSurfaceId's? They can't all be different as that would defeat the purpose of the exercise.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2508
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Chris Halse Rogers (raof) wrote : | # |
On Fri, May 1, 2015 at 12:41 PM, Daniel van Vugt
<email address hidden> wrote:
> Already confused by whether you mean internal integer ID by "ID" or
> MirSurfaceId. Please fix (3) ASAP. Having two broadly unrelated
> meanings of "surface ID" is going to keep biting us.
In this we'll assume that this app wants to do surface position/state
restore, and so is calling mir_surface_
>
> (1) What I mean is consider if you have only one app installed and do
> this:
> * Open one instance A. Close instance A.
Instance A opens surface A' and requests an ID A*, which it serialises
to disc.
> * Open two instances B and C. Close instances B and C.
App instance B loads the serialised MirSurfaceId A* from disc, sets it
on the MirSurfaceSpec, and creates surface B'. Since there is no
current surface with Id A*, the MirSurfaceId of B' is A* and it gets
the state restoration treatment. App instance B calls
request_
App instance C loads the serialised MirSurfaceId A* from disc, sets it
on the MirSurfaceSpec, and creates surface C'. Since there *is* a
current surface with Id A*, C' gets a new MirSurfaceId C* and does not
get the state restoration treatment. App instance C calls
request_
>
> * Open one instance D.
> Now exactly which instances have the same MirSurfaceId's? They can't
> all be different as that would defeat the purpose of the exercise.
App instance D loads the serialised MirSurfaceId C* from disc, sets it
on the MirSurfaceSpec, and creates surface D'. Since there is no
current surface with Id C*, D' gets Id C* and has C's state restored.
Note that more sophisticated clients could do more interesting things.
For example, if the application is a document editor then it can store
the serialised MirSurfaceId alongside the document and restore that
when appropriate. In that case, if instances A, C, D were opening the
same document and instance B opened a different document, A', C', and
D' would share ID and state, and B' would get a different ID and state.
Chris Halse Rogers (raof) wrote : | # |
Oh, and FWIW - I've had some discussions with the GNOME Wayland guys, and got a vague “yeah, that seems sensible” about sticking the relevant client API into GTK.
Daniel van Vugt (vanvugt) wrote : | # |
(0) I'm concerned that we might have missed initial analysis/
(1) OK so indeed there is only one MirSurfaceId being shared by the multiple instances of an app, as expected. Just confused because that's the opposite of your first reply to (1) depending on the meaning of "ID" (see (3)).
(2) Opt-in concerns me still. Is it really OK that only some toolkits/apps will have a nice unique UUID?
(3) I still vote for s/MirSurfaceId/
(4) You could solve the /proc/self/exe problem by using "/proc/self/exe + /proc/self/cmdline" instead. That would be different between Java apps, but also consistent and stable between shell-launched app instances.
(5) Mention of the word "disc" and files is also a concern. Last time those words came up, the consensus was that Mir should never touch disc/files because the context in which Mir is used will vary between shells and we don't want a shell having settings spread between multiple repositories. A more elegant solution is for Mir to specify just the serialization format (ie. a string) and let the shell choose the storage location (e.g. QSettings/
Chris Halse Rogers (raof) wrote : | # |
On Fri, May 1, 2015 at 3:02 PM, Daniel van Vugt
<email address hidden> wrote:
> Review: Needs Fixing
>
> (0) I'm concerned that we might have missed initial
> analysis/
> convinced him at the time that remembering things was a bad idea
> (based on complaints from users in Compiz bugs that windows opened on
> other desktops they had forgotten about). So the whole exercise of
> remembering anything is questionable, but that's not a blocking issue
> if "remembering" is an option you can turn off easily.
Relevant bit of the Mir Surface spec:
> (1) OK so indeed there is only one MirSurfaceId being shared by the
> multiple instances of an app, as expected. Just confused because
> that's the opposite of your first reply to (1) depending on the
> meaning of "ID" (see (3)).
I didn't understand the question in the same way you did. I thought it
was talking about multiple *concurrent* instances of an application; in
this case they do indeed get different IDs.
Multiple sequential instances of an application may share a surface ID,
as indeed that's one of the use-cases.
> (2) Opt-in concerns me still. Is it really OK that only some
> toolkits/apps will have a nice unique UUID?
Yes. This is no worse than it is today in X. And should be better,
because we'll actually be able to *use* the information the client
opts-into.
> (3) I still vote for s/MirSurfaceId/
> discussion is suffering from the confusion of the current name
> already. Or s/Signature/
I still don't particularly like either Signature or Fingerprint, but am
willing to be outvoted.
>
> (4) You could solve the /proc/self/exe problem by using
> "/proc/self/exe + /proc/self/cmdline" instead. That would be
> different between Java apps, but also consistent and stable between
> shell-launched app instances.
It is not stable between shell-launched app instances, at least if you
include content-hub as a part of the shell.
A substantial number of applications are not launched directly by the
shell, and it'd be particularly weird if, for example, your email app
consistently opened in a certain place when launched from the launcher
but in a different place when launched from a notification.
>
> (5) Mention of the word "disc" and files is also a concern. Last time
> those words came up, the consensus was that Mir should never touch
> disc/files because the context in which Mir is used will vary between
> shells and we don't want a shell having settings spread between
> multiple repositories. A more elegant solution is for Mir to specify
> just the serialization format (ie. a string) and let the shell choose
> the storage location (e.g. QSettings/
Every mention of “disc” in the example was a *client* writing to
disc.
In the current implementation there's (a) no write to disc shell-side,
and (b) a shell is perfectly able to implement the whole
PersistentSurfa
it w...
Chris Halse Rogers (raof) wrote : | # |
Hm. How about MirReference, rather than MirSurfaceId?
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2511
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2512
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2513
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
>>string, stored or sent to another process, and then later deserialised to refer to the same
>> MirSurface.
Or rather can be used to indicate that a new MirSurface refers to the same application window?
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2514
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
Needs Fixing (nit):
222+MirReferenc
223+ : string_
...
910- explicit FrontendShell(
911- wrapped{wrapped} {}
912+ explicit FrontendShell(
913+ std::shared_
914+ : wrapped{wrapped},
915+ surface_
etc.
Not the approved layout:
~~~~
Needs clarification:
86+/**
87+ * \brief Free a MirReference
88+ * \param [in] id The MirReference to free
89+ */
90+void mir_reference_
This is just freeing the local resource? It doesn't purge knowledge of the reference from the server?
~~~~
[non blocking opinion] I'm not convinced that "Reference" is as good a name as "ID".
Alexandros Frantzis (afrantzis) wrote : | # |
> Not the approved layout:
>
> http://
Our code base contains some of the approved style and much more of the traditional style (i.e. what is used in this MP). I personally like the traditionally style better because with it a multi-line constructor parameter list is visually distinct from the initializer list [1]. Perhaps we should revisit the decision.
Chris Halse Rogers (raof) wrote : | # |
If you'd like to *ensure* that the style used is consistent, there's always https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2518
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
(3) I agree MirSurfaceId probably needs to begin with "MirSurface" but the "Id" part is easily confused with surface IDs we already have in the server code and even visible in the client debug API. Still think MirSurfaceSignature or MirSurfaceFinge
(4) "/proc/self/exe + /proc/self/cmdline + (subwindow ID?)" instead of UUID would not only work, but it would avoid the need to generate UUIDs somewhere and would solve problem (2) instead of declaring it unsolvable. The issue of where an app was spawned from changing its ID is not an issue. The vast majority of the time a user will launch an app in the same way yielding the same ID. Arguably it's even a feature for different launchers to yield different restore positions. Using UUIDs prevents us from being able to have state memories work for 100% of apps (issue (2) is never solved). At least as a fallback from UUIDs (apps/toolkits that don't implement them) it would be a good idea.
Chris Halse Rogers (raof) wrote : | # |
On Wed, May 6, 2015 at 4:24 PM, Daniel van Vugt
<email address hidden> wrote:
> (4) "/proc/self/exe + /proc/self/cmdline + (subwindow ID?)" instead
> of UUID would not only work, but it would avoid the need to generate
> UUIDs somewhere and would solve problem (2) instead of declaring it
> unsolvable. The issue of where an app was spawned from changing its
> ID is not an issue. The vast majority of the time a user will launch
> an app in the same way yielding the same ID.
Actually, I think that for all but one of the applications I have open
*right now* /proc/self/cmdline will give the wrong result - Chrome has
been opened from xdg-open <some link>, which is not stable, QtCreator
has been opened with a file, but probably should not change positions
based on the initially opened file, geary was launched with arguments
from the indicator, …
One of the reasons why no major X window manager restores window state
is that the heuristics available are wrong too often to be useful.
I think that the foundation of your argument - that the user will
almost always launch an app with the same /proc/self/cmdline - is false.
Andreas Pokorny (andreas-pokorny) wrote : | # |
looks good
nit:two nl in frontend_shell.cpp 876 and 891
Daniel van Vugt (vanvugt) wrote : | # |
(3) Specifically you now have these two types:
mir:
MirSurfaceId
which while they sound similar are vastly different. So a different name is required to avoid any confusion. Even in a semi-public client API we already have something called surface ID:
int mir_debug_
(4) That's a reasonable argument. Also given item (0) people such as myself will disable the feature anyway so will have nothing to complain about. And so long as MirSurfaceId remains an opaque string the implementation is flexible.
Alan Griffiths (alan-griffiths) wrote : | # |
I approve of the design. A few nits:
217+#include <uuid/uuid.h>
...
289+#include <uuid/uuid.h>
unused includes
~~~~
205+ ${UUID_LDFLAGS} ${UUID_LIBRARIES}
Dependency in the wrong place - it is libmirserver that uses uuid_*
~~~~
1063+ msh::DefaultPer
"map" seems to reflect the implementation, not the usage. Would "store" or "pss" be better?
~~~~
225+MirSurfaceI
Not the approved layout:
Kevin DuBois (kdub) wrote : | # |
lgtm. I kept thinking that MirSurfaceTag might be another good name, but I'm okay with Id as well.
652+ UUID(UUID const& copy_from);
could have assignment defined too.
Alberto Aguirre (albaguirre) wrote : | # |
s/serialise/
s/serialise/
+ what Alan said.
Chris Halse Rogers (raof) wrote : | # |
Client type naming poll: I'm ok with MirSurfaceTag. I prefer the newly-thought-of MirSurfaceTicket.
Also ok with MirTag or MirTicket if we wanted the ability to be lazy in future and use these as generic identifiers; I'm weakly opposed to this on meaningful-type grounds.
Alan Griffiths (alan-griffiths) wrote : | # |
> Client type naming poll: I'm ok with MirSurfaceTag. I prefer the newly-
> thought-of MirSurfaceTicket.
>
> Also ok with MirTag or MirTicket if we wanted the ability to be lazy in future
> and use these as generic identifiers; I'm weakly opposed to this on
> meaningful-type grounds.
I'm happy with MirSurfaceID.
If we actually think there is a chance of confusion between the useful-
Alexandros Frantzis (afrantzis) wrote : | # |
My preference is for MirSurfaceSignature or MirSurfaceFinge
Alberto Aguirre (albaguirre) wrote : | # |
MirPersistentID?
MirCookie?
But I'm mostly ok with MirSurfaceID. MirSurfaceTag would be fine too.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2522
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
Also against the name "MirSurfaceId" is the widespread knowledge people have about windowing systems where window instances usually have some sort of integer ID (as they do in Mir too). But that's definitely not what this is about.
Preferences (in order):
MirSurfaceSig
MirSurfaceFin
MirSurfaceT{
Alan Griffiths (alan-griffiths) wrote : | # |
145+class PersistentSurfa
Should disable CopyAssign
~~~~
168+ /**
169+ * \brief Lookup Surface by ID.
170+ * \param [in] id ID of surface to lookup
171+ * \return The surface with ID \arg id.
172+ */
173+ virtual std::shared_
What happens if, for example, the surface has been closed since the ID was created? I'd guess a nullptr result but it needs to be documented.
~~~~
181+ * \throw std::out_of_range if \arg buffer contains a valid serialized ID, but
182+ * the PersistentSurfa
Is this actually a post-condition failure? It seems more like a possible result: AFAICS this depends on the state of external actions (e.g. the client innocently has an ID from a store that was wiped between server sessions, or for a surface that has been closed).
Perhaps the problem is that I'm not seeing the use(s) of this feature the same way that you are: surely, for the user, the point is whether the store can provide the linked data (either a surface or some surface properties) not whether the ID is recognized.
In that model is seems more reasonable for the deserialize_id() to succeed and the subsequent surface_for_id() to return nullptr. (And if that is right then maybe Ids should be a value type that validates on construction without needing to be constructed by the store?)
~~~~
Something that doesn't appear to be addressed is the destruction of IDs. Is there a mechanism for this? I realize that not invalidating IDs avoids invalid references being passed, but it seems wrong for a "persistent store" to lack such a mechanism.
Alan Griffiths (alan-griffiths) wrote : | # |
148+ /**
149+ * An opaque identifier tied to this PersistentSurfa
150+ */
151+ class Id
152+ {
153+ public:
154+ virtual ~Id() = default;
155+ virtual bool operator==(Id const& rhs) const = 0;
156+ };
...
167+ virtual Id const& id_for_
...
173+ virtual std::shared_
I guess it is techincally OK (as we can't create sliced objects because of the pure member) but use of an interface combined passing references and allowing CopyAssign seems like a confusion of value and instance semantics. (I'd much rather see either a pointer or a value type being passed.)
Daniel van Vugt (vanvugt) wrote : | # |
Yet another pre-existing definition of SurfaceId:
mir_
Best not to keep using the same name for different unrelated things.
Alan Griffiths (alan-griffiths) wrote : | # |
For the moment just a critique of the class Id semantics. Shall we sort those first?
160 + /**
161 + * \brief Acquire ID for a Surface
162 + * \param [in] surface to query or generate an ID for
163 + * \return A reference to the ID of this surface. This reference is stable
164 + * for the lifetime of the PersistentSurfa
165 + * \note If \arg surface has not yet had an ID generated, this generates its ID.
166 + */
167 + virtual Id const& id_for_
Requiring the reference to remain valid is reasonable for the user, but it requires that every ID ever referenced remains in memory. For a persistant surface store that persists to disk this could be inconvenient.
The API is also error prone:
auto const id = ps->id_
ps->
(UB because, e.g. the default implementation casts the passed reference to a UUID.)
We could break this example by giving Id a private destructor, but I think it would be better to return a value.
Making Id a value type admittedly doesn't allow the store to chose its own ID implementation, but I think is a better trade-off.
~~~~
168 + /**
169 + * \brief Lookup Surface by ID.
170 + * \param [in] id ID of surface to lookup
171 + * \return The surface with ID \arg id.
172 + */
173 + virtual std::shared_
What happens if, for example, the surface has been closed since the ID was created? I'd guess a nullptr result but it needs to be documented.
~~~~
184 + virtual Id const& deserialize_
...
191 + virtual std::vector<
Do these need to be on the PersistentSurfa
Chris Halse Rogers (raof) wrote : | # |
On Wed, May 20, 2015 at 9:46 AM, Alan Griffiths <email address hidden>
wrote:
> Review: Needs Fixing
>
> For the moment just a critique of the class Id semantics. Shall we
> sort those first?
>
> 160 + /**
> 161 + * \brief Acquire ID for a Surface
> 162 + * \param [in] surface to query or generate an ID for
> 163 + * \return A reference to the ID of this surface.
> This reference is stable
> 164 + * for the lifetime of the
> PersistentSurfa
> 165 + * \note If \arg surface has not yet had an ID generated,
> this generates its ID.
> 166 + */
> 167 + virtual Id const&
> id_for_
>
> Requiring the reference to remain valid is reasonable for the user,
> but it requires that every ID ever referenced remains in memory. For
> a persistant surface store that persists to disk this could be
> inconvenient.
>
> The API is also error prone:
>
> auto const id = ps->id_
> ps->surface_
> behaviour
>
> (UB because, e.g. the default implementation casts the passed
> reference to a UUID.)
>
> We could break this example by giving Id a private destructor, but I
> think it would be better to return a value.
>
> Making Id a value type admittedly doesn't allow the store to chose
> its own ID implementation, but I think is a better trade-off.
Yeah, I'm part-way through making Id a concrete value type backed by a
uuid_t. Stores will need to deal with it.
>
>
> ~~~~
>
> 168 + /**
> 169 + * \brief Lookup Surface by ID.
> 170 + * \param [in] id ID of surface to lookup
> 171 + * \return The surface with ID \arg id.
> 172 + */
> 173 + virtual std::shared_
> const& id) const = 0;
>
> What happens if, for example, the surface has been closed since the
> ID was created? I'd guess a nullptr result but it needs to be
> documented.
Either that or throwing; I guess returning nullptr is more idiomatic.
Will document.
>
> ~~~~
>
> 184 + virtual Id const& deserialize_
> buffer) const = 0;
> ...
> 191 + virtual std::vector<
> const = 0;
>
> Do these need to be on the PersistentSurfa
> they be functions on Id?
Yeah, now that Id is concrete they're moving to it.
Chris Halse Rogers (raof) wrote : | # |
It is resolved!
MirPersistentId is to be the name.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2529
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
A mix *Needs Discussion* and Nits:
If users are to customize the persistent id store then this needs to be configurable through mir::Server. I presume that (and corresponding acceptance) happens in the second half of persistent surface reference support?
~~~~~
168 + * \throws std::out_of_range if there is no surface with \arg id.
Is this really a postcondition failure?
I would have expected it to be "normal" not "exceptional" for an ID key supplied by another process to have no matching surface. (E.g. because the surface closed since the ID was generated.) AFAICS there is no way to "validate" the ID before making this call.
~~~~
162 + virtual Id id_for_
163 + /**
Could probably do with another newline.
~~~~
196 + std::vector<
197 + static Id deserialize_
These are members of Id, so "_id" is unnecessary repetition.
~~~~
202 + Id(std::array<char, 37> const& buffer);
Using std::array<char, ...> in the private section seems odd when the public interface is std::vector<
919 + std::array<char, 37> null_terminated
920 + std::copy(
921 + null_terminated
I'm not sure why we don't just have a public constructor with the same parameter and semantics as deserialize_id()? (Nor why we use char internally and uint8_t publicly. Indeed, isn't std::string a better fit?)
~~~~
261 +MirWaitHandle* MirSurface:
...
278 + surface.
279 + boost::
"Error invoking create surface" is misleading.
~~~~
793 +
Spurious whitespace
Chris Halse Rogers (raof) wrote : | # |
On Mon, Jun 1, 2015 at 7:47 PM, Alan Griffiths <email address hidden>
wrote:
> Review: Needs Information
>
> A mix *Needs Discussion* and Nits:
>
> If users are to customize the persistent id store then this needs to
> be configurable through mir::Server. I presume that (and
> corresponding acceptance) happens in the second half of persistent
> surface reference support?
I think it'll actually happen later; there's no reason for shells to
want to configure this until there's a client API that uses
across-
input-methods-
customisation of the store.
>
> ~~~~~
>
> 168 + * \throws std::out_of_range if there is no surface with
> \arg id.
>
> Is this really a postcondition failure?
>
> I would have expected it to be "normal" not "exceptional" for an ID
> key supplied by another process to have no matching surface. (E.g.
> because the surface closed since the ID was generated.) AFAICS there
> is no way to "validate" the ID before making this call.
In the case of a closed surface the method is documented as returning
nullptr.
Generally, I think it's worth being able to distinguish between “this
client sent you garbage” and “this client was the victim of an
unavoidable race condition”. It's reasonable to kill the client in
the former, but not the latter.
>
> 202 + Id(std::array<char, 37> const& buffer);
>
> Using std::array<char, ...> in the private section seems odd when the
> public interface is std::vector<
> copying:
>
> 919 + std::array<char, 37> null_terminated
> 920 + std::copy(
> null_terminated
> 921 + null_terminated
>
> I'm not sure why we don't just have a public constructor with the
> same parameter and semantics as deserialize_id()? (Nor why we use
> char internally and uint8_t publicly. Indeed, isn't std::string a
> better fit?)
I don't think std::string is a good fit for the
serialisation/
I like vector<uint8_t> as it makes it clear that we're dealing with
blobs of data, not strings.
Hm. That said, serialising to a string rather than bytestream is a
perfectly natural thing to do, and is how we currently use these. I'll
rename to serialize_
std::string for deserialisation.
Daniel van Vugt (vanvugt) wrote : | # |
Seems like my primary concern is addressed. Haven't re-reviewed yet but I don't need to block this.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2538
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
> > I would have expected it to be "normal" not "exceptional" for an ID
> > key supplied by another process to have no matching surface. (E.g.
> > because the surface closed since the ID was generated.) AFAICS there
> > is no way to "validate" the ID before making this call.
>
> In the case of a closed surface the method is documented as returning
> nullptr.
>
> Generally, I think it's worth being able to distinguish between “this
> client sent you garbage” and “this client was the victim of an
> unavoidable race condition”. It's reasonable to kill the client in
> the former, but not the latter.
It sounds as though I've been confused by the term "persistent" - I was thinking the ID might be used across sequential executions of the same client[1]. In which case the client can't know if the server happens to have been restarted (and lost its list of closed surface IDs).
Are these actually serializable IDs rather than persistent ones?
[1] I know that identifying surfaces doesn't make sense across different executions of a client, but we do have use cases (the WM storing defaults for a surface) where a persistent ID is needed. Are we going to have a separate "persistent surface properties ID" for that?
Chris Halse Rogers (raof) wrote : | # |
No, I don't think you've been confused, but perhaps the comment is a little confusing. Or maybe the partially completed code is a bit confusing.
The PersistentSurfa
At the moment they are indeed serializable IDs; when we implement the persistent bit they'll become persistent :).
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2539
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2540
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2540
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
So, six minutes to start glmark and then timed out after an hour. Probably unrelated to these changes.
[timestamp] Start time : 2015-06-
...
[timestamp] Start : mir_performance
+ grep -q mir_demo_client_
+ grep -q mir_privileged_
+ phablet-test-run -x -s 00693fd555c9186a -v umockdev-run -- mir_performance
running umockdev-run -- mir_performance
Running main() from command_
[0;32m[==========] [mRunning 1 test from 1 test case.
[0;32m[----------] [mGlobal test environment set-up.
[0;32m[----------] [m1 test from GLMark2Test
[0;32m[ RUN ] [mGLMark2Test.
[1433321633.435408] mirplatform: Found graphics driver: dummy
[1433321633.438794] mirplatform: Found graphics driver: android
[1433321633.439770] mirplatform: Found graphics driver: mesa
Build timed out (after 60 minutes). Marking the build as failed.
Build was aborted
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2540
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Chris Halse Rogers (raof) wrote : | # |
What the hell, mako?
/me fires up his mako to test locally.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2540
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Chris Halse Rogers (raof) wrote : | # |
Ok. This works fine on *my* mako. Let's just try prodding CI again.
Chris Halse Rogers (raof) wrote : | # |
...!
Ok. So, on more detailed inspection this *does* fail on my mako. glmark2-es2-mir, and as far as I've tested, *only* glmark2-es2-mir hangs in a syscall before displaying anything. mir_demo_
Um...
Chris Halse Rogers (raof) wrote : | # |
Oh, that's fun.
So, this is libmirprotobuf0 breakage. Because I've added an RPC call this has re-arranged the protobuf:
On desktop it just happens to work.
Now, I've moved the request_
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2542
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alberto Aguirre (albaguirre) wrote : | # |
^--Looks like device setup failure - not related to this MP. Retriggering.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2542
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-06-02 19:34:13 +0000 |
3 | +++ CMakeLists.txt 2015-06-09 07:21:31 +0000 |
4 | @@ -173,6 +173,7 @@ |
5 | find_package(LTTngUST REQUIRED) |
6 | pkg_check_modules(UDEV REQUIRED libudev) |
7 | pkg_check_modules(GLIB REQUIRED glib-2.0) |
8 | +pkg_check_modules(UUID REQUIRED uuid) |
9 | |
10 | include_directories (${GLESv2_INCLUDE_DIRS}) |
11 | include_directories (${EGL_INCLUDE_DIRS}) |
12 | |
13 | === modified file 'debian/control' |
14 | --- debian/control 2015-06-08 08:48:29 +0000 |
15 | +++ debian/control 2015-06-09 07:21:31 +0000 |
16 | @@ -40,6 +40,7 @@ |
17 | libfreetype6-dev, |
18 | abi-compliance-checker, |
19 | libevdev-dev, |
20 | + uuid-dev, |
21 | Standards-Version: 3.9.4 |
22 | Homepage: https://launchpad.net/mir |
23 | # If you aren't a member of ~mir-team but need to upload packaging changes, |
24 | |
25 | === modified file 'include/client/mir_toolkit/client_types.h' |
26 | --- include/client/mir_toolkit/client_types.h 2015-05-26 22:56:02 +0000 |
27 | +++ include/client/mir_toolkit/client_types.h 2015-06-09 07:21:31 +0000 |
28 | @@ -43,6 +43,7 @@ |
29 | typedef struct MirScreencast MirScreencast; |
30 | typedef struct MirPromptSession MirPromptSession; |
31 | typedef struct MirBufferStream MirBufferStream; |
32 | +typedef struct MirPersistentId MirPersistentId; |
33 | |
34 | /** |
35 | * Returned by asynchronous functions. Must not be free'd by |
36 | @@ -123,6 +124,10 @@ |
37 | typedef void (*mir_client_fd_callback)( |
38 | MirPromptSession *prompt_session, size_t count, int const* fds, void* context); |
39 | |
40 | + |
41 | +typedef void (*mir_surface_id_callback)( |
42 | + MirSurface* surface, MirPersistentId* id, void* context); |
43 | + |
44 | /** |
45 | * MirBufferUsage specifies how a surface can and will be used. A "hardware" |
46 | * surface can be used for OpenGL accelerated rendering. A "software" surface |
47 | |
48 | === modified file 'include/client/mir_toolkit/mir_surface.h' |
49 | --- include/client/mir_toolkit/mir_surface.h 2015-05-27 11:37:38 +0000 |
50 | +++ include/client/mir_toolkit/mir_surface.h 2015-06-09 07:21:31 +0000 |
51 | @@ -582,6 +582,46 @@ |
52 | */ |
53 | void mir_surface_apply_spec(MirSurface* surface, MirSurfaceSpec* spec); |
54 | |
55 | +/** |
56 | + * \brief Request an ID for the surface that can be shared cross-process and |
57 | + * across restarts. |
58 | + * |
59 | + * This call acquires a MirPersistentId for this MirSurface. This MirPersistentId |
60 | + * can be serialized to a string, stored or sent to another process, and then |
61 | + * later deserialized to refer to the same surface. |
62 | + * |
63 | + * \param [in] surface The surface to acquire a persistent reference to. |
64 | + * \param [in] callback Callback to invoke when the request completes. |
65 | + * \param [in,out] context User data passed to completion callback. |
66 | + * \return A MirWaitHandle that can be used in mir_wait_for to await completion. |
67 | + */ |
68 | +MirWaitHandle* mir_surface_request_persistent_id(MirSurface* surface, mir_surface_id_callback callback, void* context); |
69 | + |
70 | +/** |
71 | + * \brief Request a persistent ID for a surface and wait for the result |
72 | + * \param [in] surface The surface to acquire a persistent ID for. |
73 | + * \return A MirPersistentId. This MirPersistentId is owned by the calling code, and must |
74 | + * be freed with a call to mir_persistent_id_release() |
75 | + */ |
76 | +MirPersistentId* mir_surface_request_persistent_id_sync(MirSurface *surface); |
77 | + |
78 | +/** |
79 | + * \brief Check the validity of a MirPersistentId |
80 | + * \param [in] id The MirPersistentId |
81 | + * \return True iff the MirPersistentId contains a valid ID value. |
82 | + * |
83 | + * \note This does not guarantee that the ID refers to a currently valid object. |
84 | + */ |
85 | +bool mir_persistent_id_is_valid(MirPersistentId* id); |
86 | + |
87 | +/** |
88 | + * \brief Free a MirPersistentId |
89 | + * \param [in] id The MirPersistentId to free |
90 | + * \note This frees only the client-side representation; it has no effect on the |
91 | + * object referred to by \arg id. |
92 | + */ |
93 | +void mir_persistent_id_release(MirPersistentId* id); |
94 | + |
95 | #ifdef __cplusplus |
96 | } |
97 | /**@}*/ |
98 | |
99 | === added file 'include/server/mir/shell/persistent_surface_store.h' |
100 | --- include/server/mir/shell/persistent_surface_store.h 1970-01-01 00:00:00 +0000 |
101 | +++ include/server/mir/shell/persistent_surface_store.h 2015-06-09 07:21:31 +0000 |
102 | @@ -0,0 +1,131 @@ |
103 | +/* |
104 | + * Copyright © 2015 Canonical Ltd. |
105 | + * |
106 | + * This program is free software: you can redistribute it and/or modify |
107 | + * it under the terms of the GNU General Public License version 3 as |
108 | + * published by the Free Software Foundation. |
109 | + * |
110 | + * This program is distributed in the hope that it will be useful, |
111 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
112 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
113 | + * GNU General Public License for more details. |
114 | + * |
115 | + * You should have received a copy of the GNU General Public License |
116 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
117 | + * |
118 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
119 | + */ |
120 | + |
121 | +#ifndef MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ |
122 | +#define MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ |
123 | + |
124 | +#include <memory> |
125 | +#include <vector> |
126 | +#include <array> |
127 | +#include <uuid/uuid.h> |
128 | + |
129 | + |
130 | +namespace mir |
131 | +{ |
132 | +namespace scene |
133 | +{ |
134 | +class Surface; |
135 | +} |
136 | + |
137 | +namespace shell |
138 | +{ |
139 | +/** |
140 | + * A store for Surface information divorced from the lifetime of any given Session |
141 | + * |
142 | + * This provides the backing for persistent references to Surface objects, |
143 | + * both across client Sessions and across shell restarts, for features such as |
144 | + * surface position/size restoration. |
145 | + * |
146 | + * \todo Persistence across shell restarts is as-yet unimplemented. |
147 | + */ |
148 | +class PersistentSurfaceStore |
149 | +{ |
150 | +public: |
151 | + class Id; |
152 | + |
153 | + virtual ~PersistentSurfaceStore() = default; |
154 | + |
155 | + /** |
156 | + * \brief Acquire ID for a Surface |
157 | + * \param [in] surface Surface to query or generate an ID for |
158 | + * \return The ID for this surface. |
159 | + * \note If \arg surface has not yet had an ID generated, this generates its ID. |
160 | + * \note This does not extend the lifetime of \arg surface. |
161 | + */ |
162 | + virtual Id id_for_surface(std::shared_ptr<scene::Surface> const& surface) = 0; |
163 | + |
164 | + /** |
165 | + * \brief Lookup Surface by ID. |
166 | + * \param [in] id ID of surface to lookup |
167 | + * \return The surface with ID \arg id. If this surface has been destroyed, |
168 | + * but the store retains a reference, returns nullptr. |
169 | + * \throws std::out_of_range if the store has no reference for a surface with \arg id. |
170 | + */ |
171 | + virtual std::shared_ptr<scene::Surface> surface_for_id(Id const& id) const = 0; |
172 | +}; |
173 | +} |
174 | +} |
175 | + |
176 | +namespace std |
177 | +{ |
178 | +template<> |
179 | +struct hash<mir::shell::PersistentSurfaceStore::Id>; |
180 | +} |
181 | + |
182 | +namespace mir |
183 | +{ |
184 | +namespace shell |
185 | +{ |
186 | + |
187 | +class PersistentSurfaceStore::Id final |
188 | +{ |
189 | +public: |
190 | + /** |
191 | + * \brief Generate a new, unique Id. |
192 | + */ |
193 | + Id(); |
194 | + |
195 | + /** |
196 | + * \brief Construct an Id from its serialized string form |
197 | + * \param serialized_form [in] The previously-serialized Id |
198 | + * \throw std::invalid_argument if \arg serialized_form is not parseable as an Id. |
199 | + */ |
200 | + Id(std::string const& serialized_form); |
201 | + |
202 | + Id(Id const& rhs); |
203 | + Id& operator=(Id const& rhs); |
204 | + |
205 | + bool operator==(Id const& rhs) const; |
206 | + |
207 | + /** |
208 | + * \brief Serialize to a UTF-8 string |
209 | + * \return A string representation of the Id; this is guaranteed to be valid UTF-8 |
210 | + */ |
211 | + std::string serialize_to_string() const; |
212 | +private: |
213 | + friend struct std::hash<Id>; |
214 | + |
215 | + uuid_t uuid; |
216 | +}; |
217 | + |
218 | +} |
219 | +} |
220 | + |
221 | +namespace std |
222 | +{ |
223 | +template<> |
224 | +struct hash<mir::shell::PersistentSurfaceStore::Id> |
225 | +{ |
226 | + typedef mir::shell::PersistentSurfaceStore::Id argument_type; |
227 | + typedef std::size_t result_type; |
228 | + |
229 | + result_type operator()(argument_type const& uuid) const; |
230 | +}; |
231 | +} |
232 | + |
233 | +#endif // MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ |
234 | |
235 | === modified file 'src/client/mir_surface.cpp' |
236 | --- src/client/mir_surface.cpp 2015-05-21 19:25:51 +0000 |
237 | +++ src/client/mir_surface.cpp 2015-06-09 07:21:31 +0000 |
238 | @@ -127,6 +127,16 @@ |
239 | return message; |
240 | } |
241 | |
242 | +MirPersistentId::MirPersistentId(std::string const& string_id) |
243 | + : string_id{string_id} |
244 | +{ |
245 | +} |
246 | + |
247 | +std::string const&MirPersistentId::as_string() |
248 | +{ |
249 | + return string_id; |
250 | +} |
251 | + |
252 | MirSurface::MirSurface(std::string const& error) |
253 | { |
254 | surface.set_error(error); |
255 | @@ -220,6 +230,42 @@ |
256 | return false; |
257 | } |
258 | |
259 | +void MirSurface::acquired_persistent_id(mir_surface_id_callback callback, void* context) |
260 | +{ |
261 | + if (!persistent_id.has_error()) |
262 | + { |
263 | + callback(this, new MirPersistentId{persistent_id.value()}, context); |
264 | + } |
265 | + else |
266 | + { |
267 | + callback(this, nullptr, context); |
268 | + } |
269 | + persistent_id_wait_handle.result_received(); |
270 | +} |
271 | + |
272 | +MirWaitHandle* MirSurface::request_persistent_id(mir_surface_id_callback callback, void* context) |
273 | +{ |
274 | + std::lock_guard<decltype(mutex)> lock{mutex}; |
275 | + |
276 | + if (persistent_id.has_value()) |
277 | + { |
278 | + callback(this, new MirPersistentId{persistent_id.value()}, context); |
279 | + return nullptr; |
280 | + } |
281 | + |
282 | + persistent_id_wait_handle.expect_result(); |
283 | + try |
284 | + { |
285 | + server->request_persistent_surface_id(0, &surface.id(), &persistent_id, gp::NewCallback(this, &MirSurface::acquired_persistent_id, callback, context)); |
286 | + } |
287 | + catch (std::exception const& ex) |
288 | + { |
289 | + surface.set_error(std::string{"Failed to acquire a persistent ID from the server: "} + |
290 | + boost::diagnostic_information(ex)); |
291 | + } |
292 | + return &persistent_id_wait_handle; |
293 | +} |
294 | + |
295 | MirWaitHandle* MirSurface::get_create_wait_handle() |
296 | { |
297 | return &create_wait_handle; |
298 | |
299 | === modified file 'src/client/mir_surface.h' |
300 | --- src/client/mir_surface.h 2015-05-21 19:25:51 +0000 |
301 | +++ src/client/mir_surface.h 2015-06-09 07:21:31 +0000 |
302 | @@ -99,6 +99,17 @@ |
303 | mir::optional_value<AspectRatio> max_aspect; |
304 | }; |
305 | |
306 | +struct MirPersistentId |
307 | +{ |
308 | +public: |
309 | + MirPersistentId(std::string const& string_id); |
310 | + |
311 | + std::string const& as_string(); |
312 | + |
313 | +private: |
314 | + std::string const string_id; |
315 | +}; |
316 | + |
317 | struct MirSurface |
318 | { |
319 | public: |
320 | @@ -153,18 +164,22 @@ |
321 | MirWaitHandle* modify(MirSurfaceSpec const& changes); |
322 | |
323 | static bool is_valid(MirSurface* query); |
324 | + |
325 | + MirWaitHandle* request_persistent_id(mir_surface_id_callback callback, void* context); |
326 | private: |
327 | mutable std::mutex mutex; // Protects all members of *this |
328 | |
329 | void on_configured(); |
330 | void on_cursor_configured(); |
331 | void created(mir_surface_callback callback, void* context); |
332 | + void acquired_persistent_id(mir_surface_id_callback callback, void* context); |
333 | MirPixelFormat convert_ipc_pf_to_geometry(google::protobuf::int32 pf) const; |
334 | |
335 | mir::protobuf::DisplayServer::Stub* server{nullptr}; |
336 | mir::protobuf::Debug::Stub* debug{nullptr}; |
337 | mir::protobuf::Surface surface; |
338 | mir::protobuf::BufferRequest buffer_request; |
339 | + mir::protobuf::PersistentSurfaceId persistent_id; |
340 | std::string error_message; |
341 | std::string name; |
342 | mir::protobuf::Void void_response; |
343 | @@ -178,6 +193,7 @@ |
344 | MirWaitHandle create_wait_handle; |
345 | MirWaitHandle configure_wait_handle; |
346 | MirWaitHandle configure_cursor_wait_handle; |
347 | + MirWaitHandle persistent_id_wait_handle; |
348 | |
349 | std::shared_ptr<mir::client::ClientBufferStreamFactory> const buffer_stream_factory; |
350 | std::shared_ptr<mir::client::ClientBufferStream> buffer_stream; |
351 | |
352 | === modified file 'src/client/mir_surface_api.cpp' |
353 | --- src/client/mir_surface_api.cpp 2015-05-27 11:37:38 +0000 |
354 | +++ src/client/mir_surface_api.cpp 2015-06-09 07:21:31 +0000 |
355 | @@ -562,3 +562,40 @@ |
356 | MIR_LOG_UNCAUGHT_EXCEPTION(ex); |
357 | return false; |
358 | } |
359 | + |
360 | +MirWaitHandle* mir_surface_request_persistent_id(MirSurface* surface, mir_surface_id_callback callback, void* context) |
361 | +{ |
362 | + mir::require(mir_surface_is_valid(surface)); |
363 | + |
364 | + return surface->request_persistent_id(callback, context); |
365 | +} |
366 | + |
367 | +namespace |
368 | +{ |
369 | +void assign_surface_id_result(MirSurface*, MirPersistentId* id, void* context) |
370 | +{ |
371 | + void** result_ptr = reinterpret_cast<void**>(context); |
372 | + *result_ptr = id; |
373 | +} |
374 | +} |
375 | + |
376 | +MirPersistentId* mir_surface_request_persistent_id_sync(MirSurface *surface) |
377 | +{ |
378 | + mir::require(mir_surface_is_valid(surface)); |
379 | + |
380 | + MirPersistentId* result = nullptr; |
381 | + mir_wait_for(mir_surface_request_persistent_id(surface, |
382 | + &assign_surface_id_result, |
383 | + &result)); |
384 | + return result; |
385 | +} |
386 | + |
387 | +bool mir_persistent_id_is_valid(MirPersistentId* id) |
388 | +{ |
389 | + return id != nullptr; |
390 | +} |
391 | + |
392 | +void mir_persistent_id_release(MirPersistentId* id) |
393 | +{ |
394 | + delete id; |
395 | +} |
396 | |
397 | === modified file 'src/client/symbols.map' |
398 | --- src/client/symbols.map 2015-05-27 11:44:54 +0000 |
399 | +++ src/client/symbols.map 2015-06-09 07:21:31 +0000 |
400 | @@ -76,6 +76,8 @@ |
401 | mir_omnidirectional_resize_cursor_name; |
402 | mir_open_hand_cursor_name; |
403 | mir_orientation_event_get_direction; |
404 | + mir_persistent_id_is_valid; |
405 | + mir_persistent_id_release; |
406 | mir_platform_message_create; |
407 | mir_platform_message_get_data; |
408 | mir_platform_message_get_fds; |
409 | @@ -118,6 +120,8 @@ |
410 | mir_surface_is_valid; |
411 | mir_surface_release; |
412 | mir_surface_release_sync; |
413 | + mir_surface_request_persistent_id; |
414 | + mir_surface_request_persistent_id_sync; |
415 | mir_surface_set_event_handler; |
416 | mir_surface_set_preferred_orientation; |
417 | mir_surface_set_state; |
418 | |
419 | === modified file 'src/include/server/mir/default_server_configuration.h' |
420 | --- src/include/server/mir/default_server_configuration.h 2015-05-21 19:25:51 +0000 |
421 | +++ src/include/server/mir/default_server_configuration.h 2015-06-09 07:21:31 +0000 |
422 | @@ -83,6 +83,7 @@ |
423 | class DisplayLayout; |
424 | class HostLifecycleEventListener; |
425 | class Shell; |
426 | +class PersistentSurfaceStore; |
427 | namespace detail { class FrontendShell; } |
428 | } |
429 | namespace time |
430 | @@ -268,6 +269,7 @@ |
431 | virtual std::shared_ptr<scene::PromptSessionListener> the_prompt_session_listener(); |
432 | virtual std::shared_ptr<scene::PromptSessionManager> the_prompt_session_manager(); |
433 | virtual std::shared_ptr<shell::HostLifecycleEventListener> the_host_lifecycle_event_listener(); |
434 | + virtual std::shared_ptr<shell::PersistentSurfaceStore> the_persistent_surface_store(); |
435 | |
436 | /** @} */ |
437 | |
438 | @@ -440,6 +442,7 @@ |
439 | CachedPtr<scene::CoordinateTranslator> coordinate_translator; |
440 | CachedPtr<EmergencyCleanup> emergency_cleanup; |
441 | CachedPtr<shell::HostLifecycleEventListener> host_lifecycle_event_listener; |
442 | + CachedPtr<shell::PersistentSurfaceStore> surface_store; |
443 | CachedPtr<SharedLibraryProberReport> shared_library_prober_report; |
444 | CachedPtr<shell::Shell> shell; |
445 | |
446 | |
447 | === modified file 'src/include/server/mir/frontend/shell.h' |
448 | --- src/include/server/mir/frontend/shell.h 2015-04-08 07:51:14 +0000 |
449 | +++ src/include/server/mir/frontend/shell.h 2015-06-09 07:21:31 +0000 |
450 | @@ -25,6 +25,7 @@ |
451 | #include <sys/types.h> |
452 | |
453 | #include <memory> |
454 | +#include <vector> |
455 | |
456 | namespace mir |
457 | { |
458 | @@ -62,6 +63,7 @@ |
459 | virtual SurfaceId create_surface(std::shared_ptr<Session> const& session, scene::SurfaceCreationParameters const& params) = 0; |
460 | virtual void modify_surface(std::shared_ptr<Session> const& session, SurfaceId surface, shell::SurfaceSpecification const& modifications) = 0; |
461 | virtual void destroy_surface(std::shared_ptr<Session> const& session, SurfaceId surface) = 0; |
462 | + virtual std::string persistent_id_for(std::shared_ptr<Session> const& session, SurfaceId surface) = 0; |
463 | |
464 | virtual int set_surface_attribute( |
465 | std::shared_ptr<Session> const& session, |
466 | |
467 | === modified file 'src/protobuf/mir_protobuf.proto' |
468 | --- src/protobuf/mir_protobuf.proto 2015-06-05 06:35:11 +0000 |
469 | +++ src/protobuf/mir_protobuf.proto 2015-06-09 07:21:31 +0000 |
470 | @@ -85,6 +85,12 @@ |
471 | required int32 value = 1; |
472 | }; |
473 | |
474 | +message PersistentSurfaceId { |
475 | + optional string value = 1; |
476 | + |
477 | + optional string error = 127; |
478 | +}; |
479 | + |
480 | message BufferStreamId { |
481 | required int32 value = 1; |
482 | }; |
483 | @@ -321,7 +327,10 @@ |
484 | rpc start_prompt_session(PromptSessionParameters) returns (Void); |
485 | rpc stop_prompt_session(Void) returns (Void); |
486 | rpc exchange_buffer(BufferRequest) returns (Buffer); |
487 | + |
488 | rpc submit_buffer(BufferRequest) returns (Void); |
489 | + |
490 | + rpc request_persistent_surface_id(SurfaceId) returns (PersistentSurfaceId); |
491 | } |
492 | |
493 | message CoordinateTranslationRequest { |
494 | |
495 | === modified file 'src/server/CMakeLists.txt' |
496 | --- src/server/CMakeLists.txt 2015-05-19 21:34:34 +0000 |
497 | +++ src/server/CMakeLists.txt 2015-06-09 07:21:31 +0000 |
498 | @@ -76,6 +76,7 @@ |
499 | ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} |
500 | ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} |
501 | ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} |
502 | + ${UUID_LDFLAGS} ${UUID_LIBRARIES} |
503 | ) |
504 | |
505 | set(MIR_SERVER_OBJECTS ${MIR_SERVER_OBJECTS} PARENT_SCOPE) |
506 | @@ -101,6 +102,7 @@ |
507 | ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} |
508 | ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} |
509 | ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} |
510 | + ${UUID_LDFLAGS} ${UUID_LIBRARIES} |
511 | ) |
512 | |
513 | install(TARGETS mirserver |
514 | |
515 | === modified file 'src/server/frontend/protobuf_message_processor.cpp' |
516 | --- src/server/frontend/protobuf_message_processor.cpp 2015-06-08 13:43:27 +0000 |
517 | +++ src/server/frontend/protobuf_message_processor.cpp 2015-06-09 07:21:31 +0000 |
518 | @@ -312,6 +312,10 @@ |
519 | report->exception_handled(display_server.get(), invocation.id(), err); |
520 | } |
521 | } |
522 | + else if ("request_persistent_surface_id" == invocation.method_name()) |
523 | + { |
524 | + invoke(this, display_server.get(), &protobuf::DisplayServer::request_persistent_surface_id, invocation); |
525 | + } |
526 | else |
527 | { |
528 | report->unknown_method(display_server.get(), invocation.id(), invocation.method_name()); |
529 | |
530 | === modified file 'src/server/frontend/session_mediator.cpp' |
531 | --- src/server/frontend/session_mediator.cpp 2015-06-05 08:29:23 +0000 |
532 | +++ src/server/frontend/session_mediator.cpp 2015-06-09 07:21:31 +0000 |
533 | @@ -878,6 +878,24 @@ |
534 | done->Run(); |
535 | } |
536 | |
537 | +void mf::SessionMediator::request_persistent_surface_id( |
538 | + ::google::protobuf::RpcController*, |
539 | + mir::protobuf::SurfaceId const* request, |
540 | + mir::protobuf::PersistentSurfaceId* response, |
541 | + google::protobuf::Closure* done) |
542 | +{ |
543 | + auto const session = weak_session.lock(); |
544 | + |
545 | + if (!session) |
546 | + BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); |
547 | + |
548 | + auto buffer = shell->persistent_id_for(session, mf::SurfaceId{request->value()}); |
549 | + |
550 | + *response->mutable_value() = std::string{buffer.begin(), buffer.end()}; |
551 | + |
552 | + done->Run(); |
553 | +} |
554 | + |
555 | void mf::SessionMediator::pack_protobuf_buffer( |
556 | protobuf::Buffer& protobuf_buffer, |
557 | graphics::Buffer* graphics_buffer, |
558 | |
559 | === modified file 'src/server/frontend/session_mediator.h' |
560 | --- src/server/frontend/session_mediator.h 2015-06-01 14:37:00 +0000 |
561 | +++ src/server/frontend/session_mediator.h 2015-06-09 07:21:31 +0000 |
562 | @@ -214,6 +214,12 @@ |
563 | ::mir::protobuf::CoordinateTranslationResponse* response, |
564 | ::google::protobuf::Closure *done) override; |
565 | |
566 | + void request_persistent_surface_id( |
567 | + ::google::protobuf::RpcController* controller, |
568 | + ::mir::protobuf::SurfaceId const* request, |
569 | + ::mir::protobuf::PersistentSurfaceId* response, |
570 | + ::google::protobuf::Closure* done) override; |
571 | + |
572 | private: |
573 | void pack_protobuf_buffer(protobuf::Buffer& protobuf_buffer, |
574 | graphics::Buffer* graphics_buffer, |
575 | |
576 | === modified file 'src/server/frontend/shell_wrapper.cpp' |
577 | --- src/server/frontend/shell_wrapper.cpp 2015-04-08 07:51:14 +0000 |
578 | +++ src/server/frontend/shell_wrapper.cpp 2015-06-09 07:21:31 +0000 |
579 | @@ -68,6 +68,11 @@ |
580 | wrapped->destroy_surface(session, surface); |
581 | } |
582 | |
583 | +std::string mf::ShellWrapper::persistent_id_for(std::shared_ptr<Session> const& session, mf::SurfaceId surface) |
584 | +{ |
585 | + return wrapped->persistent_id_for(session, surface); |
586 | +} |
587 | + |
588 | int mf::ShellWrapper::set_surface_attribute( |
589 | std::shared_ptr<Session> const& session, |
590 | SurfaceId surface_id, |
591 | |
592 | === modified file 'src/server/frontend/shell_wrapper.h' |
593 | --- src/server/frontend/shell_wrapper.h 2015-04-08 07:51:14 +0000 |
594 | +++ src/server/frontend/shell_wrapper.h 2015-06-09 07:21:31 +0000 |
595 | @@ -57,6 +57,8 @@ |
596 | |
597 | void destroy_surface(std::shared_ptr<Session> const& session, SurfaceId surface) override; |
598 | |
599 | + std::string persistent_id_for(std::shared_ptr<Session> const& session, SurfaceId surface) override; |
600 | + |
601 | int set_surface_attribute( |
602 | std::shared_ptr<Session> const& session, |
603 | SurfaceId surface_id, |
604 | |
605 | === modified file 'src/server/shell/CMakeLists.txt' |
606 | --- src/server/shell/CMakeLists.txt 2015-05-19 21:42:19 +0000 |
607 | +++ src/server/shell/CMakeLists.txt 2015-06-09 07:21:31 +0000 |
608 | @@ -10,6 +10,8 @@ |
609 | default_window_manager.cpp |
610 | shell_wrapper.cpp |
611 | surface_ready_observer.cpp |
612 | + default_persistent_surface_store.cpp |
613 | + persistent_surface_store.cpp |
614 | ) |
615 | |
616 | add_library( |
617 | |
618 | === modified file 'src/server/shell/default_configuration.cpp' |
619 | --- src/server/shell/default_configuration.cpp 2015-05-19 21:34:34 +0000 |
620 | +++ src/server/shell/default_configuration.cpp 2015-06-09 07:21:31 +0000 |
621 | @@ -24,6 +24,7 @@ |
622 | #include "mir/shell/canonical_window_manager.h" |
623 | #include "mir/input/composite_event_filter.h" |
624 | #include "mir/shell/abstract_shell.h" |
625 | +#include "default_persistent_surface_store.h" |
626 | #include "frontend_shell.h" |
627 | #include "graphics_display_layout.h" |
628 | |
629 | @@ -62,12 +63,22 @@ |
630 | return wrapped; |
631 | } |
632 | |
633 | +std::shared_ptr<msh::PersistentSurfaceStore> |
634 | +mir::DefaultServerConfiguration::the_persistent_surface_store() |
635 | +{ |
636 | + return surface_store([]() |
637 | + { |
638 | + return std::make_shared<msh::DefaultPersistentSurfaceStore>(); |
639 | + }); |
640 | +} |
641 | + |
642 | std::shared_ptr<mf::Shell> |
643 | mir::DefaultServerConfiguration::the_frontend_shell() |
644 | { |
645 | return frontend_shell([this] |
646 | { |
647 | - return std::make_shared<msh::detail::FrontendShell>(the_shell()); |
648 | + return std::make_shared<msh::detail::FrontendShell>(the_shell(), |
649 | + the_persistent_surface_store()); |
650 | }); |
651 | } |
652 | |
653 | |
654 | === added file 'src/server/shell/default_persistent_surface_store.cpp' |
655 | --- src/server/shell/default_persistent_surface_store.cpp 1970-01-01 00:00:00 +0000 |
656 | +++ src/server/shell/default_persistent_surface_store.cpp 2015-06-09 07:21:31 +0000 |
657 | @@ -0,0 +1,91 @@ |
658 | +/* |
659 | + * Copyright © 2015 Canonical Ltd. |
660 | + * |
661 | + * This program is free software: you can redistribute it and/or modify |
662 | + * it under the terms of the GNU General Public License version 3 as |
663 | + * published by the Free Software Foundation. |
664 | + * |
665 | + * This program is distributed in the hope that it will be useful, |
666 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
667 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
668 | + * GNU General Public License for more details. |
669 | + * |
670 | + * You should have received a copy of the GNU General Public License |
671 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
672 | + * |
673 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
674 | + */ |
675 | + |
676 | +#include "default_persistent_surface_store.h" |
677 | +#include <uuid/uuid.h> |
678 | +#include <algorithm> |
679 | +#include <unordered_map> |
680 | +#include <boost/throw_exception.hpp> |
681 | + |
682 | +namespace msh = mir::shell; |
683 | +namespace ms = mir::scene; |
684 | + |
685 | +class msh::DefaultPersistentSurfaceStore::SurfaceIdBimap |
686 | +{ |
687 | +public: |
688 | + using Id = PersistentSurfaceStore::Id; |
689 | + |
690 | + Id const& insert_or_retrieve(std::shared_ptr<scene::Surface> const& surface); |
691 | + |
692 | + std::shared_ptr<scene::Surface> operator[](Id const& id) const; |
693 | + Id const& operator[](std::shared_ptr<scene::Surface> const& surface) const; |
694 | + |
695 | +private: |
696 | + std::unordered_map<Id, std::weak_ptr<scene::Surface>> id_to_surface; |
697 | + std::unordered_map<scene::Surface const*, Id const*> surface_to_id; |
698 | +}; |
699 | + |
700 | +auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::insert_or_retrieve(std::shared_ptr<scene::Surface> const& surface) |
701 | + -> Id const& |
702 | +{ |
703 | + auto element = surface_to_id.insert(std::make_pair(surface.get(), nullptr)); |
704 | + if (!element.second) |
705 | + { |
706 | + // Surface already exists in our map, return it. |
707 | + return *element.first->second; |
708 | + } |
709 | + else |
710 | + { |
711 | + auto new_element = id_to_surface.insert(std::make_pair(Id{}, surface)); |
712 | + // Update the surface_to_id map element from nullptr to our new Id |
713 | + element.first->second = &new_element.first->first; |
714 | + return *element.first->second; |
715 | + } |
716 | +} |
717 | + |
718 | +auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::operator[](std::shared_ptr<scene::Surface> const& surface) const |
719 | + -> Id const& |
720 | +{ |
721 | + return *surface_to_id.at(surface.get()); |
722 | +} |
723 | + |
724 | +auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::operator[](Id const& id) const |
725 | + -> std::shared_ptr<scene::Surface> |
726 | +{ |
727 | + return id_to_surface.at(id).lock(); |
728 | +} |
729 | + |
730 | +msh::DefaultPersistentSurfaceStore::DefaultPersistentSurfaceStore() |
731 | + : store{std::make_unique<SurfaceIdBimap>()} |
732 | +{ |
733 | +} |
734 | + |
735 | +msh::DefaultPersistentSurfaceStore::~DefaultPersistentSurfaceStore() |
736 | +{ |
737 | +} |
738 | + |
739 | +auto msh::DefaultPersistentSurfaceStore::id_for_surface(std::shared_ptr<scene::Surface> const& surface) |
740 | + -> Id |
741 | +{ |
742 | + return store->insert_or_retrieve(surface); |
743 | +} |
744 | + |
745 | +std::shared_ptr<ms::Surface> msh::DefaultPersistentSurfaceStore::surface_for_id(Id const& id) const |
746 | +{ |
747 | + return (*store)[id]; |
748 | +} |
749 | |
750 | === added file 'src/server/shell/default_persistent_surface_store.h' |
751 | --- src/server/shell/default_persistent_surface_store.h 1970-01-01 00:00:00 +0000 |
752 | +++ src/server/shell/default_persistent_surface_store.h 2015-06-09 07:21:31 +0000 |
753 | @@ -0,0 +1,44 @@ |
754 | +/* |
755 | + * Copyright © 2015 Canonical Ltd. |
756 | + * |
757 | + * This program is free software: you can redistribute it and/or modify |
758 | + * it under the terms of the GNU General Public License version 3 as |
759 | + * published by the Free Software Foundation. |
760 | + * |
761 | + * This program is distributed in the hope that it will be useful, |
762 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
763 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
764 | + * GNU General Public License for more details. |
765 | + * |
766 | + * You should have received a copy of the GNU General Public License |
767 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
768 | + * |
769 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
770 | + */ |
771 | + |
772 | +#ifndef MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ |
773 | +#define MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ |
774 | + |
775 | +#include "mir/shell/persistent_surface_store.h" |
776 | + |
777 | +namespace mir |
778 | +{ |
779 | +namespace shell |
780 | +{ |
781 | +class DefaultPersistentSurfaceStore : public PersistentSurfaceStore |
782 | +{ |
783 | +public: |
784 | + DefaultPersistentSurfaceStore(); |
785 | + ~DefaultPersistentSurfaceStore() override; |
786 | + |
787 | + Id id_for_surface(std::shared_ptr<scene::Surface> const& surface) override; |
788 | + std::shared_ptr<scene::Surface> surface_for_id(Id const& id) const override; |
789 | + |
790 | +private: |
791 | + class SurfaceIdBimap; |
792 | + std::unique_ptr<SurfaceIdBimap> const store; |
793 | +}; |
794 | +} |
795 | +} |
796 | + |
797 | +#endif // MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ |
798 | |
799 | === modified file 'src/server/shell/frontend_shell.cpp' |
800 | --- src/server/shell/frontend_shell.cpp 2015-04-08 07:51:14 +0000 |
801 | +++ src/server/shell/frontend_shell.cpp 2015-06-09 07:21:31 +0000 |
802 | @@ -17,6 +17,7 @@ |
803 | */ |
804 | |
805 | #include "frontend_shell.h" |
806 | +#include "mir/shell/persistent_surface_store.h" |
807 | #include "mir/shell/shell.h" |
808 | |
809 | #include "mir/scene/session.h" |
810 | @@ -93,6 +94,14 @@ |
811 | wrapped->destroy_surface(scene_session, surface); |
812 | } |
813 | |
814 | +std::string msh::FrontendShell::persistent_id_for(std::shared_ptr<mf::Session> const& session, mf::SurfaceId surface_id) |
815 | +{ |
816 | + auto const scene_session = std::dynamic_pointer_cast<ms::Session>(session); |
817 | + auto const surface = scene_session->surface(surface_id); |
818 | + |
819 | + return surface_store->id_for_surface(surface).serialize_to_string(); |
820 | +} |
821 | + |
822 | int msh::FrontendShell::set_surface_attribute( |
823 | std::shared_ptr<mf::Session> const& session, |
824 | mf::SurfaceId surface_id, |
825 | |
826 | === modified file 'src/server/shell/frontend_shell.h' |
827 | --- src/server/shell/frontend_shell.h 2015-04-08 07:51:14 +0000 |
828 | +++ src/server/shell/frontend_shell.h 2015-06-09 07:21:31 +0000 |
829 | @@ -29,6 +29,7 @@ |
830 | namespace shell |
831 | { |
832 | class Shell; |
833 | +class PersistentSurfaceStore; |
834 | |
835 | namespace detail |
836 | { |
837 | @@ -36,9 +37,14 @@ |
838 | struct FrontendShell : mf::Shell |
839 | { |
840 | std::shared_ptr<shell::Shell> const wrapped; |
841 | + std::shared_ptr<shell::PersistentSurfaceStore> const surface_store; |
842 | |
843 | - explicit FrontendShell(std::shared_ptr<shell::Shell> const& wrapped) : |
844 | - wrapped{wrapped} {} |
845 | + explicit FrontendShell(std::shared_ptr<shell::Shell> const& wrapped, |
846 | + std::shared_ptr<shell::PersistentSurfaceStore> const& surface_store) |
847 | + : wrapped{wrapped}, |
848 | + surface_store{surface_store} |
849 | + { |
850 | + } |
851 | |
852 | std::shared_ptr<mf::Session> open_session( |
853 | pid_t client_pid, |
854 | @@ -63,6 +69,8 @@ |
855 | |
856 | void destroy_surface(std::shared_ptr<mf::Session> const& session, mf::SurfaceId surface) override; |
857 | |
858 | + std::string persistent_id_for(std::shared_ptr<mf::Session> const& session, mf::SurfaceId surface) override; |
859 | + |
860 | int set_surface_attribute( |
861 | std::shared_ptr<mf::Session> const& session, |
862 | mf::SurfaceId surface_id, |
863 | |
864 | === added file 'src/server/shell/persistent_surface_store.cpp' |
865 | --- src/server/shell/persistent_surface_store.cpp 1970-01-01 00:00:00 +0000 |
866 | +++ src/server/shell/persistent_surface_store.cpp 2015-06-09 07:21:31 +0000 |
867 | @@ -0,0 +1,79 @@ |
868 | +/* |
869 | + * Copyright © 2015 Canonical Ltd. |
870 | + * |
871 | + * This program is free software: you can redistribute it and/or modify |
872 | + * it under the terms of the GNU General Public License version 3 as |
873 | + * published by the Free Software Foundation. |
874 | + * |
875 | + * This program is distributed in the hope that it will be useful, |
876 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
877 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
878 | + * GNU General Public License for more details. |
879 | + * |
880 | + * You should have received a copy of the GNU General Public License |
881 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
882 | + * |
883 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
884 | + */ |
885 | + |
886 | +#include "mir/shell/persistent_surface_store.h" |
887 | + |
888 | +#include <boost/throw_exception.hpp> |
889 | + |
890 | +namespace msh = mir::shell; |
891 | +using Id = mir::shell::PersistentSurfaceStore::Id; |
892 | + |
893 | +Id::Id() |
894 | +{ |
895 | + uuid_generate(uuid); |
896 | +} |
897 | + |
898 | +Id::Id(std::string const& serialized_form) |
899 | +{ |
900 | + using namespace std::literals::string_literals; |
901 | + if (serialized_form.size() != 36) |
902 | + { |
903 | + BOOST_THROW_EXCEPTION((std::invalid_argument{"Failed to parse: "s + serialized_form + |
904 | + " (has invalid length: "s + std::to_string(serialized_form.size()) + |
905 | + " expected 36)"})); |
906 | + } |
907 | + |
908 | + if (uuid_parse(serialized_form.c_str(), uuid) != 0) |
909 | + { |
910 | + BOOST_THROW_EXCEPTION((std::invalid_argument{"Failed to parse: "s + serialized_form})); |
911 | + } |
912 | +} |
913 | + |
914 | +Id::Id(Id const& rhs) |
915 | +{ |
916 | + std::copy(rhs.uuid, rhs.uuid + sizeof(rhs.uuid), uuid); |
917 | +} |
918 | + |
919 | +Id& Id::operator=(Id const& rhs) |
920 | +{ |
921 | + std::copy(rhs.uuid, rhs.uuid + sizeof(rhs.uuid), uuid); |
922 | + return *this; |
923 | +} |
924 | + |
925 | +bool Id::operator==(Id const& rhs) const |
926 | +{ |
927 | + return uuid_compare(uuid, rhs.uuid) == 0; |
928 | +} |
929 | + |
930 | +std::string Id::serialize_to_string() const |
931 | +{ |
932 | + // uuid_unparse adds a trailing null; allocate enough memory for it... |
933 | + char buffer[37]; |
934 | + uuid_unparse(uuid, buffer); |
935 | + |
936 | + return buffer; |
937 | +} |
938 | + |
939 | +auto std::hash<Id>::operator()(argument_type const &uuid) const |
940 | + -> result_type |
941 | +{ |
942 | + // uuid_t is defined in the header to be a char[16]; hash this as two 64-byte integers |
943 | + // and mix the result with XOR. |
944 | + return std::hash<uint64_t>()(*reinterpret_cast<uint64_t const*>(uuid.uuid)) ^ |
945 | + std::hash<uint64_t>()(*reinterpret_cast<uint64_t const*>(uuid.uuid + sizeof(uint64_t))); |
946 | +} |
947 | |
948 | === modified file 'src/server/symbols.map' |
949 | --- src/server/symbols.map 2015-06-02 13:41:40 +0000 |
950 | +++ src/server/symbols.map 2015-06-09 07:21:31 +0000 |
951 | @@ -581,6 +581,7 @@ |
952 | mir::DefaultServerConfiguration::the_mediating_display_changer*; |
953 | mir::DefaultServerConfiguration::the_message_processor_report*; |
954 | mir::DefaultServerConfiguration::the_options*; |
955 | + mir::DefaultServerConfiguration::the_persistent_surface_store*; |
956 | mir::DefaultServerConfiguration::the_pixel_buffer*; |
957 | mir::DefaultServerConfiguration::the_placement_strategy*; |
958 | mir::DefaultServerConfiguration::the_prompt_connection_creator*; |
959 | |
960 | === modified file 'tests/acceptance-tests/test_client_library.cpp' |
961 | --- tests/acceptance-tests/test_client_library.cpp 2015-05-27 11:37:38 +0000 |
962 | +++ tests/acceptance-tests/test_client_library.cpp 2015-06-09 07:21:31 +0000 |
963 | @@ -885,3 +885,23 @@ |
964 | mir_surface_release_sync(surface); |
965 | mir_connection_release(connection); |
966 | } |
967 | + |
968 | +TEST_F(ClientLibrary, can_get_persistent_surface_id) |
969 | +{ |
970 | + auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
971 | + |
972 | + auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, |
973 | + 800, 600, |
974 | + mir_pixel_format_argb_8888); |
975 | + auto surface = mir_surface_create_sync(surface_spec); |
976 | + mir_surface_spec_release(surface_spec); |
977 | + |
978 | + ASSERT_THAT(surface, IsValid()); |
979 | + |
980 | + auto surface_id = mir_surface_request_persistent_id_sync(surface); |
981 | + EXPECT_TRUE(mir_persistent_id_is_valid(surface_id)); |
982 | + |
983 | + mir_surface_release_sync(surface); |
984 | + mir_persistent_id_release(surface_id); |
985 | + mir_connection_release(connection); |
986 | +} |
987 | |
988 | === modified file 'tests/include/mir_test_doubles/mock_shell.h' |
989 | --- tests/include/mir_test_doubles/mock_shell.h 2015-04-08 07:51:14 +0000 |
990 | +++ tests/include/mir_test_doubles/mock_shell.h 2015-06-09 07:21:31 +0000 |
991 | @@ -54,6 +54,8 @@ |
992 | MOCK_METHOD2(create_surface, frontend::SurfaceId(std::shared_ptr<frontend::Session> const&, scene::SurfaceCreationParameters const& params)); |
993 | MOCK_METHOD3(modify_surface, void(std::shared_ptr<frontend::Session> const&, frontend::SurfaceId, shell::SurfaceSpecification const&)); |
994 | MOCK_METHOD2(destroy_surface, void(std::shared_ptr<frontend::Session> const&, frontend::SurfaceId)); |
995 | + MOCK_METHOD2(persistent_id_for, std::string(std::shared_ptr<frontend::Session> const&, frontend::SurfaceId)); |
996 | + |
997 | |
998 | MOCK_METHOD4(set_surface_attribute, int( |
999 | std::shared_ptr<frontend::Session> const& session, frontend::SurfaceId surface_id, |
1000 | |
1001 | === modified file 'tests/unit-tests/shell/CMakeLists.txt' |
1002 | --- tests/unit-tests/shell/CMakeLists.txt 2015-03-31 02:35:42 +0000 |
1003 | +++ tests/unit-tests/shell/CMakeLists.txt 2015-06-09 07:21:31 +0000 |
1004 | @@ -2,6 +2,8 @@ |
1005 | ${CMAKE_CURRENT_SOURCE_DIR}/test_default_placement_strategy.cpp |
1006 | ${CMAKE_CURRENT_SOURCE_DIR}/test_default_window_manager.cpp |
1007 | ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_display_layout.cpp |
1008 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_persistent_surface_store_id.cpp |
1009 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_default_persistent_surface_store.cpp |
1010 | ) |
1011 | |
1012 | set( |
1013 | |
1014 | === added file 'tests/unit-tests/shell/test_default_persistent_surface_store.cpp' |
1015 | --- tests/unit-tests/shell/test_default_persistent_surface_store.cpp 1970-01-01 00:00:00 +0000 |
1016 | +++ tests/unit-tests/shell/test_default_persistent_surface_store.cpp 2015-06-09 07:21:31 +0000 |
1017 | @@ -0,0 +1,124 @@ |
1018 | +/* |
1019 | + * Copyright © 2015 Canonical Ltd. |
1020 | + * |
1021 | + * This program is free software: you can redistribute it and/or modify |
1022 | + * it under the terms of the GNU General Public License version 3 as |
1023 | + * published by the Free Software Foundation. |
1024 | + * |
1025 | + * This program is distributed in the hope that it will be useful, |
1026 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1027 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1028 | + * GNU General Public License for more details. |
1029 | + * |
1030 | + * You should have received a copy of the GNU General Public License |
1031 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1032 | + * |
1033 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1034 | + */ |
1035 | + |
1036 | +#include "mir/shell/persistent_surface_store.h" |
1037 | + |
1038 | +#include "mir/scene/surface.h" |
1039 | + |
1040 | +#include "src/server/shell/default_persistent_surface_store.h" |
1041 | + |
1042 | +#include "mir_test_doubles/mock_surface.h" |
1043 | + |
1044 | + |
1045 | +#include <gmock/gmock.h> |
1046 | +#include <gtest/gtest.h> |
1047 | + |
1048 | +namespace ms = mir::scene; |
1049 | +namespace msh = mir::shell; |
1050 | +namespace mtd = mir::test::doubles; |
1051 | + |
1052 | +TEST(DefaultPersistentSurfaceStore, id_for_surface_is_idempotent) |
1053 | +{ |
1054 | + using namespace testing; |
1055 | + |
1056 | + msh::DefaultPersistentSurfaceStore store; |
1057 | + |
1058 | + auto surface = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1059 | + |
1060 | + auto id_one = store.id_for_surface(surface); |
1061 | + auto id_two = store.id_for_surface(surface); |
1062 | + |
1063 | + EXPECT_THAT(id_one, Eq(std::ref(id_two))); |
1064 | +} |
1065 | + |
1066 | +TEST(DefaultPersistentSurfaceStore, id_is_stable_under_aliasing) |
1067 | +{ |
1068 | + using namespace testing; |
1069 | + |
1070 | + msh::DefaultPersistentSurfaceStore store; |
1071 | + |
1072 | + auto surface = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1073 | + auto surface_alias = surface; |
1074 | + |
1075 | + auto id_one = store.id_for_surface(surface); |
1076 | + auto id_two = store.id_for_surface(surface_alias); |
1077 | + |
1078 | + EXPECT_THAT(id_one, Eq(std::ref(id_two))); |
1079 | +} |
1080 | + |
1081 | +TEST(DefaultPersistentSurfaceStore, can_lookup_surface_by_id) |
1082 | +{ |
1083 | + using namespace testing; |
1084 | + |
1085 | + msh::DefaultPersistentSurfaceStore store; |
1086 | + |
1087 | + auto surface = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1088 | + |
1089 | + auto id = store.id_for_surface(surface); |
1090 | + |
1091 | + auto looked_up_surface = store.surface_for_id(id); |
1092 | + |
1093 | + EXPECT_THAT(looked_up_surface, Eq(surface)); |
1094 | +} |
1095 | + |
1096 | +TEST(DefaultPersistentSurfaceStore, retrieves_correct_surface) |
1097 | +{ |
1098 | + using namespace testing; |
1099 | + |
1100 | + msh::DefaultPersistentSurfaceStore store; |
1101 | + |
1102 | + auto surface_one = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1103 | + auto surface_two = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1104 | + |
1105 | + auto id_one = store.id_for_surface(surface_one); |
1106 | + auto id_two = store.id_for_surface(surface_two); |
1107 | + |
1108 | + auto looked_up_surface_one = store.surface_for_id(id_one); |
1109 | + auto looked_up_surface_two = store.surface_for_id(id_two); |
1110 | + |
1111 | + EXPECT_THAT(looked_up_surface_one, Eq(surface_one)); |
1112 | + EXPECT_THAT(looked_up_surface_two, Eq(surface_two)); |
1113 | +} |
1114 | + |
1115 | +TEST(DefaultPersistentSurfaceStore, looking_up_destroyed_surface_returns_nullptr) |
1116 | +{ |
1117 | + using namespace testing; |
1118 | + |
1119 | + msh::DefaultPersistentSurfaceStore store; |
1120 | + |
1121 | + auto surface = std::make_shared<NiceMock<mtd::MockSurface>>(); |
1122 | + |
1123 | + auto id = store.id_for_surface(surface); |
1124 | + |
1125 | + surface.reset(); |
1126 | + |
1127 | + auto looked_up_surface = store.surface_for_id(id); |
1128 | + |
1129 | + EXPECT_THAT(looked_up_surface, Eq(nullptr)); |
1130 | +} |
1131 | + |
1132 | +TEST(DefaultPersistentSurfaceStore, looking_up_nonexistent_surface_throws) |
1133 | +{ |
1134 | + using namespace testing; |
1135 | + |
1136 | + msh::DefaultPersistentSurfaceStore store; |
1137 | + |
1138 | + msh::PersistentSurfaceStore::Id id; |
1139 | + |
1140 | + EXPECT_THROW(store.surface_for_id(id), std::out_of_range); |
1141 | +} |
1142 | |
1143 | === added file 'tests/unit-tests/shell/test_persistent_surface_store_id.cpp' |
1144 | --- tests/unit-tests/shell/test_persistent_surface_store_id.cpp 1970-01-01 00:00:00 +0000 |
1145 | +++ tests/unit-tests/shell/test_persistent_surface_store_id.cpp 2015-06-09 07:21:31 +0000 |
1146 | @@ -0,0 +1,88 @@ |
1147 | +/* |
1148 | + * Copyright © 2015 Canonical Ltd. |
1149 | + * |
1150 | + * This program is free software: you can redistribute it and/or modify |
1151 | + * it under the terms of the GNU General Public License version 3 as |
1152 | + * published by the Free Software Foundation. |
1153 | + * |
1154 | + * This program is distributed in the hope that it will be useful, |
1155 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1156 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1157 | + * GNU General Public License for more details. |
1158 | + * |
1159 | + * You should have received a copy of the GNU General Public License |
1160 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1161 | + * |
1162 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1163 | + */ |
1164 | + |
1165 | +#include "mir/shell/persistent_surface_store.h" |
1166 | + |
1167 | +#include <gmock/gmock.h> |
1168 | +#include <gtest/gtest.h> |
1169 | + |
1170 | +namespace ms = mir::scene; |
1171 | +namespace msh = mir::shell; |
1172 | +//namespace mtd = mir::test::doubles; |
1173 | + |
1174 | +using Id = msh::PersistentSurfaceStore::Id; |
1175 | + |
1176 | +TEST(PersistentSurfaceStoreId, deserialising_wildly_incorrect_buffer_raises_exception) |
1177 | +{ |
1178 | + EXPECT_THROW(Id{"bob"}, std::invalid_argument); |
1179 | +} |
1180 | + |
1181 | +TEST(PersistentSurfaceStoreId, deserialising_invalid_buffer_raises_exception) |
1182 | +{ |
1183 | + // This is the right size, but isn't a UUID because it lacks the XX-XX-XX structure |
1184 | + EXPECT_THROW(Id{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, std::invalid_argument); |
1185 | +} |
1186 | + |
1187 | +TEST(PersistentSurfaceStoreId, serialization_roundtrips_with_deserialization) |
1188 | +{ |
1189 | + using namespace testing; |
1190 | + Id first_id; |
1191 | + |
1192 | + auto const buf = first_id.serialize_to_string(); |
1193 | + Id const second_id{buf}; |
1194 | + |
1195 | + EXPECT_THAT(second_id, Eq(first_id)); |
1196 | +} |
1197 | + |
1198 | +TEST(PersistentSurfaceStoreId, ids_assigned_evaluate_equal) |
1199 | +{ |
1200 | + using namespace testing; |
1201 | + |
1202 | + Id const first_id; |
1203 | + |
1204 | + auto const second_id = first_id; |
1205 | + |
1206 | + EXPECT_THAT(second_id, Eq(first_id)); |
1207 | +} |
1208 | + |
1209 | +TEST(PersistentSurfaceStoreId, equal_ids_hash_equally) |
1210 | +{ |
1211 | + using namespace testing; |
1212 | + |
1213 | + auto const uuid_string = "0744caf3-c8d9-4483-a005-3375c1954287"; |
1214 | + |
1215 | + Id const first_id{uuid_string}; |
1216 | + Id const second_id{uuid_string}; |
1217 | + |
1218 | + EXPECT_THAT(std::hash<Id>()(second_id), Eq(std::hash<Id>()(first_id))); |
1219 | +} |
1220 | + |
1221 | +TEST(PersistentSurfaceStoreId, can_assign_ids) |
1222 | +{ |
1223 | + using namespace testing; |
1224 | + |
1225 | + Id first_id; |
1226 | + Id second_id; |
1227 | + |
1228 | + // Technically, there's a roughly 1-in-2^128 chance of a false fail here. |
1229 | + EXPECT_THAT(second_id, Not(Eq(first_id))); |
1230 | + |
1231 | + second_id = first_id; |
1232 | + |
1233 | + EXPECT_THAT(second_id, Eq(first_id)); |
1234 | +} |
Using UUIDs only makes sense if "persistent" also means persistent between runs, and persistent across all machines. Obviously a unique ID for the lifetime of the server process could be achieved with an int...
(1) So if you need a persistent ID across machines and server instances, what happens if you have multiple instances of a client running? They'd have the same IDs, which would be a problem right?
(2) What about simple apps/toolkits that don't support the UUID? For example X apps. Can we live with some windows not having IDs? In the past I've suggested the fallback should be some string that just describes the window title and default attributes, which would at least be consistent between runs. So that's "mir_surface_ id_from_ string" ? But there's no good guarantee of uniqueness.
(3) Given we can't have a 100% water-tight guarantee that this new MirSurfaceId is unique (per (2)), it would need to co-exist with and never replace our internal integer IDs. So in that case it's not really the "surface ID" but should be called something else like "MirSurfaceSign ature". So at least someone looking at the Mir source code knows the surface ID is not the surface signature.
(1) and (2) need info. (3) I think needs fixing.