Mir

Merge lp:~mir-team/mir/improve-attribute-protocol-and-normalize-getters into lp:mir

Proposed by Robert Carr
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: no longer in the source branch.
Merged at revision: 1778
Proposed branch: lp:~mir-team/mir/improve-attribute-protocol-and-normalize-getters
Merge into: lp:mir
Diff against target: 933 lines (+383/-171)
16 files modified
include/client/mir_toolkit/mir_surface.h (+14/-0)
include/server/mir/frontend/surface.h (+1/-0)
include/shared/mir_toolkit/common.h (+4/-0)
include/test/mir_test_doubles/mock_frontend_surface.h (+1/-0)
include/test/mir_test_doubles/stub_scene_surface.h (+1/-0)
src/client/mir_surface.cpp (+6/-3)
src/client/mir_surface.h (+3/-0)
src/client/mir_surface_api.cpp (+49/-10)
src/server/frontend/session_mediator.cpp (+9/-0)
src/server/scene/basic_surface.cpp (+125/-62)
src/server/scene/basic_surface.h (+9/-8)
src/shared/protobuf/mir_protobuf.proto (+2/-0)
tests/acceptance-tests/test_client_surface_visibility.cpp (+4/-1)
tests/unit-tests/client/test_client_mir_surface.cpp (+38/-26)
tests/unit-tests/scene/test_basic_surface.cpp (+117/-15)
tests/unit-tests/scene/test_surface_impl.cpp (+0/-46)
To merge this branch: bzr merge lp:~mir-team/mir/improve-attribute-protocol-and-normalize-getters
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Alexandros Frantzis (community) Approve
Daniel van Vugt Abstain
Chris Halse Rogers Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+225912@code.launchpad.net

Commit message

Improve surface attribute protocol and add missing getters.

Description of the change

The motivation for this branch begins with attempting to address bug:

https://bugs.launchpad.net/mir/+bug/1336553

Seems simple enough at first but on investigation noticed a few irregularities in the surface attributes:

1. Some like focus, have no getter/persistent state.
2. Some like visibility have no getter.
3. Some like DPI have a getter on the client side, and thus support querying via the use of a "-1" value.
4. Some irregular observer behavior (missing observer calls, irregular behavior when values don't change).
5. Implementing client side getters requires use of a -1 value type trick, and leads to potentially blocking getters.

This branch sets out to clear up some of these abnormalities. You can see a series of tests instantiated for each attribute in test_basic_surface. There are a few small observer fixes, etc as mentioned. In order to make DPI behave normally (i.e. throw on invalid value rather than interpret as query), I've had to change surface attributes to be sent at surface creation time. This way the client library never has to query, it always has a value for each attribute.

Focus and visibility getters are added to the client library.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Just some quick comments:

(1) This should be 0..N:
+ /* Do not specify values...code relies on 1...N ordering. */

(2) The suffix "_state" is unnecessary and potentially confusing with the attribute called "state":
15 +MirSurfaceFocusState mir_surface_get_focus_state(MirSurface *surface);
23 +MirSurfaceVisibility mir_surface_get_visibility_state(MirSurface *surface);

(3) Returning an integer value outside the range of an enum is a violation of the implicit contract that a well designed API will only return values of those enums:
13 + * -1 if the surface is invalid.
21 + * -1 if the surface is invalid.
I think a more sensible fallback for invalid surfaces would be to return mir_surface_unfocused and mir_surface_visibility_occluded.

(4) src/shared/protobuf/mir_protobuf.proto: Protocol ABI change!
Please either avoid protocol changes, or record the change explicitly with a bump of libmirprotobuf0 to libmirprotobuf1. Not ideal.

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: 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
Chris Halse Rogers (raof) wrote :

Yay! Surface attributes populated on construction! I'm broadly in favour of this :)

Your API docs don't match the behaviour; they say you'll return -1 for invalid surfaces.

While we're at it, I thought that, waaaaay back in London, we were going to abort() when passed an invalid surface? I'm very much against silently pretending that everything's OK. Likewise, I cringe at all the

try { <something> }
catch(...)
{
    //la di da! The client doesn't need to know about this...
}

in there (even though much of it is pre-existing).

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

370 + if (!set_focus_state(static_cast<MirSurfaceFocusState>(value)))
371 + BOOST_THROW_EXCEPTION(std::logic_error("Invalid focus state."));
372 + result = value;
373 break;
374 case mir_surface_attrib_swapinterval:
375 - allow_dropping = (value == 0);
376 - allow_framedropping(allow_dropping);
377 + if (!set_swap_interval(value))
378 + BOOST_THROW_EXCEPTION(std::logic_error("Invalid swapinterval"));
379 result = value;
380 break;
381 case mir_surface_attrib_dpi:
382 - if (value >= 0 && !set_dpi(value)) // Negative means query only
383 + if (!set_dpi(value))
384 BOOST_THROW_EXCEPTION(std::logic_error("Invalid DPI value"));

configure() is the only user of the set_* functions and it doesn't really need the return value. We might as well throw from the set_* functions instead of checking the return value and throwing in configure.

394 + switch (attrib)
395 + {
396 + case mir_surface_attrib_type:
397 + return type_value;
398 + case mir_surface_attrib_state:
399 + return state_value;
...

It seems like we storing the attributes in an array indexed by the attribute instead of separate variables would make life easier for BasicSurface.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Refactored the set_ functions

Added attribute array (caught several more missing locks in the meantime).

Removed misleading comment.

Lets do something about client library errors...but not in this branch...was our plan in london written down? I'll see what I can remember over lunch...I don't remember each detail.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Re: client errors - in one of those enthusiastic London days we decided that API constructors would never return NULL, but instead return an error object. Because this makes it very easy to write programs that fail in unexpected ways, we further decided that passing an error object into any API call that isn't explicitly a check for error status would die, so that developers get immediate and useful corefile-based feedback.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Bah. I was going to suggest std::array<int, > for the attrib array on the basis that operator[] would do nice bounds checking for us, but it doesn't because of silliness.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(4) Hmm, the protocol change uses "repeated" which actually appears to be backward compatible, so no ABI bump for libmirprotobuf is required after all;
https://developers.google.com/protocol-buffers/docs/proto#updating

(5) This single-use function doesn't need to exist. If you initialize your variables in the constructor then it's more obvious locking isn't required either...
ms::BasicSurface::initialize_attributes()

(6) I think the TODO comment is still valid and should not be deleted:
395 - /*
396 - * TODO: In future, query the shell implementation for the subset of
397 - * attributes/types it implements.
398 - */

Needs^H^H^H^H^HWants fixing, somewhat. I haven't gone back through in enough detail to approve yet but won't block any more.

review: Abstain
Revision history for this message
Chris Halse Rogers (raof) wrote :

The protocol change is the sort of thing that we won't be able to get away with when used on the desktop, but because the phone reboots to do any package update it's ok (as long as libmirclient updates in lockstep with libmirserver!)

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

Looks good.

Nit:
260 + std::lock_guard<std::mutex> lg(guard);

As mentioned by Daniel, locking is not currently needed.

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

+ MirSurfaceType set_type(MirSurfaceType t); // Use configure() to make public changes
+ MirSurfaceState set_state(MirSurfaceState s);
+ int set_dpi(int);
+ MirSurfaceVisibility set_visibility(MirSurfaceVisibility v);
+ int set_swap_interval(int);
+ MirSurfaceFocusState set_focus_state(MirSurfaceFocusState f);

these look weird to me - why returning the value set? Could a different value be set than what I asked for? I'd be suspicious of it, but it's private so meh.
LGTM otherwise

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Gerry:
We return the value applied because it might be different to the requested value, in some restricted shells. That's the TODO I was referring to above in (6).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/client/mir_toolkit/mir_surface.h'
2--- include/client/mir_toolkit/mir_surface.h 2014-06-19 16:15:42 +0000
3+++ include/client/mir_toolkit/mir_surface.h 2014-07-09 21:43:55 +0000
4@@ -253,6 +253,20 @@
5 * \return The DPI of the surface, or zero if unknown.
6 */
7 int mir_surface_get_dpi(MirSurface* surface);
8+
9+/**
10+ * Query the focus state for a surface.
11+ * \param [in] surface The surface to operate on
12+ * \return The focus state of said surface
13+ */
14+MirSurfaceFocusState mir_surface_get_focus(MirSurface *surface);
15+
16+/**
17+ * Query the visibility state for a surface.
18+ * \param [in] surface The surface to operate on
19+ * \return The visibility state of said surface
20+ */
21+MirSurfaceVisibility mir_surface_get_visibility(MirSurface *surface);
22
23 /**
24 * Choose the cursor state for a surface: whether a cursor is shown,
25
26=== modified file 'include/server/mir/frontend/surface.h'
27--- include/server/mir/frontend/surface.h 2014-06-09 17:35:20 +0000
28+++ include/server/mir/frontend/surface.h 2014-07-09 21:43:55 +0000
29@@ -53,6 +53,7 @@
30 virtual int client_input_fd() const = 0;
31
32 virtual int configure(MirSurfaceAttrib attrib, int value) = 0;
33+ virtual int query(MirSurfaceAttrib attrib) = 0;
34
35 virtual void set_cursor_image(std::shared_ptr<graphics::CursorImage> const& image) = 0;
36
37
38=== modified file 'include/shared/mir_toolkit/common.h'
39--- include/shared/mir_toolkit/common.h 2014-06-23 02:57:39 +0000
40+++ include/shared/mir_toolkit/common.h 2014-07-09 21:43:55 +0000
41@@ -33,12 +33,14 @@
42 */
43 typedef enum MirSurfaceAttrib
44 {
45+ /* Do not specify values...code relies on 0...N ordering. */
46 mir_surface_attrib_type,
47 mir_surface_attrib_state,
48 mir_surface_attrib_swapinterval,
49 mir_surface_attrib_focus,
50 mir_surface_attrib_dpi,
51 mir_surface_attrib_visibility,
52+ /* Must be last */
53 mir_surface_attribs
54 } MirSurfaceAttrib;
55
56@@ -68,6 +70,8 @@
57 mir_surface_states
58 } MirSurfaceState;
59
60+/* TODO: MirSurfaceFocusState MirSurfaceVisibility and MirLifecycleState use an inconsistent
61+ naming convention. */
62 typedef enum MirSurfaceFocusState
63 {
64 mir_surface_unfocused = 0,
65
66=== modified file 'include/test/mir_test_doubles/mock_frontend_surface.h'
67--- include/test/mir_test_doubles/mock_frontend_surface.h 2014-06-03 17:00:23 +0000
68+++ include/test/mir_test_doubles/mock_frontend_surface.h 2014-07-09 21:43:55 +0000
69@@ -50,6 +50,7 @@
70 MOCK_METHOD1(set_cursor_image, void(std::shared_ptr<graphics::CursorImage> const&));
71
72 MOCK_METHOD2(configure, int(MirSurfaceAttrib, int));
73+ MOCK_METHOD1(query, int(MirSurfaceAttrib));
74 };
75 }
76 }
77
78=== modified file 'include/test/mir_test_doubles/stub_scene_surface.h'
79--- include/test/mir_test_doubles/stub_scene_surface.h 2014-06-23 22:25:17 +0000
80+++ include/test/mir_test_doubles/stub_scene_surface.h 2014-07-09 21:43:55 +0000
81@@ -96,6 +96,7 @@
82 bool supports_input() const override { return true;}
83 int client_input_fd() const override { return fd;}
84 int configure(MirSurfaceAttrib, int) override { return 0; }
85+ int query(MirSurfaceAttrib) override { return 0; }
86 void with_most_recent_buffer_do(std::function<void(graphics::Buffer&)> const& ) override {}
87 };
88
89
90=== modified file 'src/client/mir_surface.cpp'
91--- src/client/mir_surface.cpp 2014-06-23 02:57:39 +0000
92+++ src/client/mir_surface.cpp 2014-07-09 21:43:55 +0000
93@@ -67,9 +67,6 @@
94
95 for (int i = 0; i < mir_surface_attribs; i++)
96 attrib_cache[i] = -1;
97- attrib_cache[mir_surface_attrib_type] = mir_surface_type_normal;
98- attrib_cache[mir_surface_attrib_state] = mir_surface_state_unknown;
99- attrib_cache[mir_surface_attrib_swapinterval] = 1;
100
101 std::lock_guard<decltype(handle_mutex)> lock(handle_mutex);
102 valid_surfaces.insert(this);
103@@ -228,6 +225,12 @@
104
105 process_incoming_buffer();
106 accelerated_window = platform->create_egl_native_window(this);
107+
108+ for(int i = 0; i < surface.attributes_size(); i++)
109+ {
110+ auto const& attrib = surface.attributes(i);
111+ attrib_cache[attrib.attrib()] = attrib.ivalue();
112+ }
113 }
114
115 connection->on_surface_created(id(), this);
116
117=== modified file 'src/client/mir_surface.h'
118--- src/client/mir_surface.h 2014-06-23 02:57:39 +0000
119+++ src/client/mir_surface.h 2014-07-09 21:43:55 +0000
120@@ -86,7 +86,10 @@
121 EGLNativeWindowType generate_native_window();
122
123 MirWaitHandle* configure(MirSurfaceAttrib a, int value);
124+
125+ // Non-blocking
126 int attrib(MirSurfaceAttrib a) const;
127+
128 MirOrientation get_orientation() const;
129
130 MirWaitHandle* configure_cursor(MirCursorConfiguration const* cursor);
131
132=== modified file 'src/client/mir_surface_api.cpp'
133--- src/client/mir_surface_api.cpp 2014-06-19 16:15:42 +0000
134+++ src/client/mir_surface_api.cpp 2014-07-09 21:43:55 +0000
135@@ -247,34 +247,73 @@
136
137 int mir_surface_get_swapinterval(MirSurface* surf)
138 {
139- return surf ? surf->attrib(mir_surface_attrib_swapinterval) : -1;
140+ int swap_interval = -1;
141+
142+ try
143+ {
144+ swap_interval = surf ? surf->attrib(mir_surface_attrib_swapinterval) : -1;
145+ }
146+ catch (...)
147+ {
148+ }
149+
150+ return swap_interval;
151 }
152
153 int mir_surface_get_dpi(MirSurface* surf)
154 {
155- int dpi = 0;
156+ int dpi = -1;
157
158 try
159 {
160 if (surf)
161 {
162 dpi = surf->attrib(mir_surface_attrib_dpi);
163- if (dpi < 0)
164- {
165- // Officially we don't support setting DPI from the client.
166- // But this is a convenient way to query it on startup...
167- surf->configure(mir_surface_attrib_dpi, -1)->wait_for_all();
168- dpi = surf->attrib(mir_surface_attrib_dpi);
169- }
170 }
171 }
172- catch (std::exception const&)
173+ catch (...)
174 {
175 }
176
177 return dpi;
178 }
179
180+MirSurfaceFocusState mir_surface_get_focus(MirSurface* surf)
181+{
182+ MirSurfaceFocusState state = mir_surface_unfocused;
183+
184+ try
185+ {
186+ if (surf)
187+ {
188+ state = static_cast<MirSurfaceFocusState>(surf->attrib(mir_surface_attrib_focus));
189+ }
190+ }
191+ catch (...)
192+ {
193+ }
194+
195+ return state;
196+}
197+
198+MirSurfaceVisibility mir_surface_get_visibility(MirSurface* surf)
199+{
200+ MirSurfaceVisibility state = static_cast<MirSurfaceVisibility>(mir_surface_visibility_occluded);
201+
202+ try
203+ {
204+ if (surf)
205+ {
206+ state = static_cast<MirSurfaceVisibility>(surf->attrib(mir_surface_attrib_visibility));
207+ }
208+ }
209+ catch (...)
210+ {
211+ }
212+
213+ return state;
214+}
215+
216 MirWaitHandle* mir_surface_configure_cursor(MirSurface* surface, MirCursorConfiguration const* cursor)
217 {
218 MirWaitHandle *result = nullptr;
219
220=== modified file 'src/server/frontend/session_mediator.cpp'
221--- src/server/frontend/session_mediator.cpp 2014-07-09 10:48:47 +0000
222+++ src/server/frontend/session_mediator.cpp 2014-07-09 21:43:55 +0000
223@@ -196,6 +196,15 @@
224
225 if (surface->supports_input())
226 response->add_fd(surface->client_input_fd());
227+
228+ for (unsigned int i = 0; i < mir_surface_attribs; i++)
229+ {
230+ auto setting = response->add_attributes();
231+
232+ setting->mutable_surfaceid()->set_value(surf_id.as_value());
233+ setting->set_attrib(i);
234+ setting->set_ivalue(surface->query(static_cast<MirSurfaceAttrib>(i)));
235+ }
236
237 advance_buffer(surf_id, *surface,
238 [lock, this, response, done, session]
239
240=== modified file 'src/server/scene/basic_surface.cpp'
241--- src/server/scene/basic_surface.cpp 2014-07-09 07:54:29 +0000
242+++ src/server/scene/basic_surface.cpp 2014-07-09 21:43:55 +0000
243@@ -159,15 +159,24 @@
244 input_sender(input_sender),
245 configurator(configurator),
246 cursor_image_(cursor_image),
247- report(report),
248- type_value(mir_surface_type_normal),
249- state_value(mir_surface_state_restored),
250- visibility_value(mir_surface_visibility_exposed),
251- dpi_value(0)
252+ report(report)
253 {
254+ initialize_attributes();
255 report->surface_created(this, surface_name);
256 }
257
258+void ms::BasicSurface::initialize_attributes()
259+{
260+ std::lock_guard<std::mutex> lg(guard);
261+
262+ attrib_values[mir_surface_attrib_type] = mir_surface_type_normal;
263+ attrib_values[mir_surface_attrib_state] = mir_surface_state_restored;
264+ attrib_values[mir_surface_attrib_swapinterval] = 1;
265+ attrib_values[mir_surface_attrib_focus] = mir_surface_unfocused;
266+ attrib_values[mir_surface_attrib_dpi] = 0;
267+ attrib_values[mir_surface_attrib_visibility] = mir_surface_visibility_exposed;
268+}
269+
270 void ms::BasicSurface::force_requests_to_complete()
271 {
272 surface_buffer_stream->force_requests_to_complete();
273@@ -404,42 +413,94 @@
274
275
276 MirSurfaceType ms::BasicSurface::type() const
277-{
278- return type_value;
279+{
280+ std::unique_lock<std::mutex> lg(guard);
281+ return static_cast<MirSurfaceType>(attrib_values[mir_surface_attrib_type]);
282 }
283
284-bool ms::BasicSurface::set_type(MirSurfaceType t)
285+MirSurfaceType ms::BasicSurface::set_type(MirSurfaceType t)
286 {
287- bool valid = false;
288-
289- if (t >= 0 && t < mir_surface_types)
290- {
291- type_value = t;
292- valid = true;
293- }
294-
295- return valid;
296+ std::unique_lock<std::mutex> lg(guard);
297+
298+ if (t < 0 || t > mir_surface_types)
299+ {
300+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface "
301+ "type."));
302+ }
303+
304+ if (attrib_values[mir_surface_attrib_type] != t)
305+ {
306+ attrib_values[mir_surface_attrib_type] = t;
307+ lg.unlock();
308+
309+ observers.attrib_changed(mir_surface_attrib_type, attrib_values[mir_surface_attrib_type]);
310+ }
311+
312+ return t;
313 }
314
315 MirSurfaceState ms::BasicSurface::state() const
316 {
317- return state_value;
318+ std::unique_lock<std::mutex> lg(guard);
319+ return static_cast<MirSurfaceState>(attrib_values[mir_surface_attrib_state]);
320 }
321
322-bool ms::BasicSurface::set_state(MirSurfaceState s)
323+MirSurfaceState ms::BasicSurface::set_state(MirSurfaceState s)
324 {
325- bool valid = false;
326+ if (s < mir_surface_state_unknown || s > mir_surface_states)
327+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface state."));
328
329- if (s > mir_surface_state_unknown &&
330- s < mir_surface_states)
331+ std::unique_lock<std::mutex> lg(guard);
332+ if (attrib_values[mir_surface_attrib_state] != s)
333 {
334- state_value = s;
335- valid = true;
336-
337+ attrib_values[mir_surface_attrib_state] = s;
338+ lg.unlock();
339+
340 observers.attrib_changed(mir_surface_attrib_state, s);
341 }
342
343- return valid;
344+ return s;
345+}
346+
347+int ms::BasicSurface::set_swap_interval(int interval)
348+{
349+ if (interval < 0)
350+ {
351+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid swapinterval"));
352+ }
353+
354+ std::unique_lock<std::mutex> lg(guard);
355+ if (attrib_values[mir_surface_attrib_swapinterval] != interval)
356+ {
357+ attrib_values[mir_surface_attrib_swapinterval] = interval;
358+ bool allow_dropping = (interval == 0);
359+ allow_framedropping(allow_dropping);
360+
361+ lg.unlock();
362+ observers.attrib_changed(mir_surface_attrib_swapinterval, interval);
363+ }
364+
365+ return interval;
366+}
367+
368+MirSurfaceFocusState ms::BasicSurface::set_focus_state(MirSurfaceFocusState new_state)
369+{
370+ if (new_state != mir_surface_focused &&
371+ new_state != mir_surface_unfocused)
372+ {
373+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid focus state."));
374+ }
375+
376+ std::unique_lock<std::mutex> lg(guard);
377+ if (attrib_values[mir_surface_attrib_focus] != new_state)
378+ {
379+ attrib_values[mir_surface_attrib_focus] = new_state;
380+
381+ lg.unlock();
382+ observers.attrib_changed(mir_surface_attrib_focus, new_state);
383+ }
384+
385+ return new_state;
386 }
387
388 void ms::BasicSurface::take_input_focus(std::shared_ptr<msh::InputTargeter> const& targeter)
389@@ -449,43 +510,26 @@
390
391 int ms::BasicSurface::configure(MirSurfaceAttrib attrib, int value)
392 {
393- bool allow_dropping = false;
394-
395- /*
396- * TODO: In future, query the shell implementation for the subset of
397- * attributes/types it implements.
398- */
399- int result = value = configurator->select_attribute_value(*this, attrib, value);
400+ int result = configurator->select_attribute_value(*this, attrib, value);
401 switch (attrib)
402 {
403 case mir_surface_attrib_type:
404- if (!set_type(static_cast<MirSurfaceType>(value)))
405- BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface "
406- "type."));
407- result = type();
408+ result = set_type(static_cast<MirSurfaceType>(result));
409 break;
410 case mir_surface_attrib_state:
411- if (value != mir_surface_state_unknown &&
412- !set_state(static_cast<MirSurfaceState>(value)))
413- BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface state."));
414- result = state();
415+ result = set_state(static_cast<MirSurfaceState>(result));
416 break;
417 case mir_surface_attrib_focus:
418- observers.attrib_changed(attrib, value);
419+ result = set_focus_state(static_cast<MirSurfaceFocusState>(result));
420 break;
421 case mir_surface_attrib_swapinterval:
422- allow_dropping = (value == 0);
423- allow_framedropping(allow_dropping);
424- result = value;
425+ result = set_swap_interval(result);
426 break;
427 case mir_surface_attrib_dpi:
428- if (value >= 0 && !set_dpi(value)) // Negative means query only
429- BOOST_THROW_EXCEPTION(std::logic_error("Invalid DPI value"));
430- result = dpi();
431+ result = set_dpi(result);
432 break;
433 case mir_surface_attrib_visibility:
434- set_visibility(static_cast<MirSurfaceVisibility>(value));
435- result = visibility_value;
436+ result = set_visibility(static_cast<MirSurfaceVisibility>(result));
437 break;
438 default:
439 BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface "
440@@ -498,6 +542,16 @@
441 return result;
442 }
443
444+int ms::BasicSurface::query(MirSurfaceAttrib attrib)
445+{
446+ if (attrib < 0 || attrib > mir_surface_attribs)
447+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface "
448+ "attribute."));
449+
450+ std::unique_lock<std::mutex> lg(guard);
451+ return attrib_values[attrib];
452+}
453+
454 void ms::BasicSurface::hide()
455 {
456 set_hidden(true);
457@@ -528,24 +582,29 @@
458
459 int ms::BasicSurface::dpi() const
460 {
461- return dpi_value;
462+ std::unique_lock<std::mutex> lock(guard);
463+ return attrib_values[mir_surface_attrib_dpi];
464 }
465
466-bool ms::BasicSurface::set_dpi(int new_dpi)
467+int ms::BasicSurface::set_dpi(int new_dpi)
468 {
469- bool valid = false;
470+ if (new_dpi < 0)
471+ {
472+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid DPI value"));
473+ }
474
475- if (new_dpi >= 0)
476+ std::unique_lock<std::mutex> lg(guard);
477+ if (attrib_values[mir_surface_attrib_dpi] != new_dpi)
478 {
479- dpi_value = new_dpi;
480- valid = true;
481- observers.attrib_changed(mir_surface_attrib_dpi, dpi_value);
482+ attrib_values[mir_surface_attrib_dpi] = new_dpi;
483+ lg.unlock();
484+ observers.attrib_changed(mir_surface_attrib_dpi, new_dpi);
485 }
486
487- return valid;
488+ return new_dpi;
489 }
490
491-void ms::BasicSurface::set_visibility(MirSurfaceVisibility new_visibility)
492+MirSurfaceVisibility ms::BasicSurface::set_visibility(MirSurfaceVisibility new_visibility)
493 {
494 if (new_visibility != mir_surface_visibility_occluded &&
495 new_visibility != mir_surface_visibility_exposed)
496@@ -553,11 +612,15 @@
497 BOOST_THROW_EXCEPTION(std::logic_error("Invalid visibility value"));
498 }
499
500- if (visibility_value != new_visibility)
501+ std::unique_lock<std::mutex> lg(guard);
502+ if (attrib_values[mir_surface_attrib_visibility] != new_visibility)
503 {
504- visibility_value = new_visibility;
505- observers.attrib_changed(mir_surface_attrib_visibility, visibility_value);
506+ attrib_values[mir_surface_attrib_visibility] = new_visibility;
507+ lg.unlock();
508+ observers.attrib_changed(mir_surface_attrib_visibility, attrib_values[mir_surface_attrib_visibility]);
509 }
510+
511+ return new_visibility;
512 }
513
514 void ms::BasicSurface::add_observer(std::shared_ptr<SurfaceObserver> const& observer)
515
516=== modified file 'src/server/scene/basic_surface.h'
517--- src/server/scene/basic_surface.h 2014-06-27 13:40:34 +0000
518+++ src/server/scene/basic_surface.h 2014-07-09 21:43:55 +0000
519@@ -139,6 +139,7 @@
520 MirSurfaceState state() const override;
521 void take_input_focus(std::shared_ptr<shell::InputTargeter> const& targeter) override;
522 int configure(MirSurfaceAttrib attrib, int value) override;
523+ int query(MirSurfaceAttrib attrib) override;
524 void hide() override;
525 void show() override;
526
527@@ -152,10 +153,12 @@
528
529 private:
530 bool visible(std::unique_lock<std::mutex>&) const;
531- bool set_type(MirSurfaceType t); // Use configure() to make public changes
532- bool set_state(MirSurfaceState s);
533- bool set_dpi(int);
534- void set_visibility(MirSurfaceVisibility v);
535+ MirSurfaceType set_type(MirSurfaceType t); // Use configure() to make public changes
536+ MirSurfaceState set_state(MirSurfaceState s);
537+ int set_dpi(int);
538+ MirSurfaceVisibility set_visibility(MirSurfaceVisibility v);
539+ int set_swap_interval(int);
540+ MirSurfaceFocusState set_focus_state(MirSurfaceFocusState f);
541
542 SurfaceObservers observers;
543 std::mutex mutable guard;
544@@ -175,10 +178,8 @@
545 std::shared_ptr<graphics::CursorImage> cursor_image_;
546 std::shared_ptr<SceneReport> const report;
547
548- MirSurfaceType type_value;
549- MirSurfaceState state_value;
550- MirSurfaceVisibility visibility_value;
551- int dpi_value;
552+ void initialize_attributes();
553+ int attrib_values[mir_surface_attribs];
554 };
555
556 }
557
558=== modified file 'src/shared/protobuf/mir_protobuf.proto'
559--- src/shared/protobuf/mir_protobuf.proto 2014-07-09 07:54:29 +0000
560+++ src/shared/protobuf/mir_protobuf.proto 2014-07-09 21:43:55 +0000
561@@ -103,6 +103,8 @@
562
563 repeated sint32 fd = 7;
564 optional int32 fds_on_side_channel = 8;
565+
566+ repeated SurfaceSetting attributes = 9;
567
568 optional string error = 127;
569 }
570
571=== modified file 'tests/acceptance-tests/test_client_surface_visibility.cpp'
572--- tests/acceptance-tests/test_client_surface_visibility.cpp 2014-06-23 17:19:22 +0000
573+++ tests/acceptance-tests/test_client_surface_visibility.cpp 2014-07-09 21:43:55 +0000
574@@ -293,7 +293,10 @@
575 mt::WaitCondition event_received;
576
577 EXPECT_CALL(mock_visibility_callback, handle(surface, visibility))
578- .WillOnce(mt::WakeUp(&event_received));
579+ .WillOnce(DoAll(Invoke([&visibility](MirSurface *s, MirSurfaceVisibility)
580+ {
581+ EXPECT_EQ(visibility, mir_surface_get_visibility(s));
582+ }), mt::WakeUp(&event_received)));
583
584 action();
585
586
587=== modified file 'tests/unit-tests/client/test_client_mir_surface.cpp'
588--- tests/unit-tests/client/test_client_mir_surface.cpp 2014-06-24 12:55:16 +0000
589+++ tests/unit-tests/client/test_client_mir_surface.cpp 2014-07-09 21:43:55 +0000
590@@ -36,11 +36,12 @@
591 #include "mir_test/gmock_fixes.h"
592 #include "mir_test/fake_shared.h"
593
594-#include <cstring>
595-
596 #include <gtest/gtest.h>
597 #include <gmock/gmock.h>
598
599+#include <cstring>
600+#include <map>
601+
602 #include <fcntl.h>
603
604 namespace mcl = mir::client;
605@@ -96,6 +97,8 @@
606 int pf_sent;
607 int stride_sent;
608
609+ static std::map<int, int> sent_surface_attributes;
610+
611 private:
612 void generate_unique_buffer()
613 {
614@@ -143,16 +146,26 @@
615 response->set_width(server_package.width);
616 response->set_height(server_package.height);
617 }
618-
619+
620 void create_surface_response(mir::protobuf::Surface* response)
621 {
622+ unsigned int const id = 2;
623+
624 response->set_fds_on_side_channel(1);
625
626- response->mutable_id()->set_value(2);
627+ response->mutable_id()->set_value(id);
628 response->set_width(width_sent);
629 response->set_height(height_sent);
630 response->set_pixel_format(pf_sent);
631 response->add_fd(input_fd);
632+
633+ for (auto const& kv : sent_surface_attributes)
634+ {
635+ auto setting = response->add_attributes();
636+ setting->mutable_surfaceid()->set_value(id);
637+ setting->set_attrib(kv.first);
638+ setting->set_ivalue(kv.second);
639+ }
640
641 create_buffer_response(response->mutable_buffer());
642 }
643@@ -167,6 +180,15 @@
644 int global_buffer_id;
645 };
646
647+std::map<int, int> MockServerPackageGenerator::sent_surface_attributes = {
648+ { mir_surface_attrib_type, mir_surface_type_normal },
649+ { mir_surface_attrib_state, mir_surface_state_restored },
650+ { mir_surface_attrib_swapinterval, 1 },
651+ { mir_surface_attrib_focus, mir_surface_focused },
652+ { mir_surface_attrib_dpi, 19 },
653+ { mir_surface_attrib_visibility, mir_surface_visibility_exposed }
654+};
655+
656 struct MockBuffer : public mcl::ClientBuffer
657 {
658 MockBuffer()
659@@ -469,6 +491,18 @@
660 create_and_wait_for_surface_with(*client_comm_channel, mock_buffer_factory);
661 }
662
663+TEST_F(MirClientSurfaceTest, attributes_set_on_surface_creation)
664+{
665+ using namespace testing;
666+
667+ auto surface = create_and_wait_for_surface_with(*client_comm_channel, mock_buffer_factory);
668+
669+ for (int i = 0; i < mir_surface_attribs; i++)
670+ {
671+ EXPECT_EQ(MockServerPackageGenerator::sent_surface_attributes[i], surface->attrib(static_cast<MirSurfaceAttrib>(i)));
672+ }
673+}
674+
675 TEST_F(MirClientSurfaceTest, create_wait_handle_really_blocks)
676 {
677 using namespace testing;
678@@ -630,28 +664,6 @@
679 EXPECT_THAT(after.height, Eq(new_height));
680 }
681
682-TEST_F(MirClientSurfaceTest, default_surface_type)
683-{
684- using namespace testing;
685-
686- auto const surface = create_and_wait_for_surface_with(*client_comm_channel, stub_buffer_factory);
687-
688- EXPECT_THAT(surface->attrib(mir_surface_attrib_type),
689- Eq(mir_surface_type_normal));
690-}
691-
692-TEST_F(MirClientSurfaceTest, default_surface_state)
693-{
694- using namespace testing;
695-
696- auto const surface = create_and_wait_for_surface_with(*client_comm_channel, stub_buffer_factory);
697-
698- // Test the default cached state value. It is always unknown until we
699- // get a real answer from the server.
700- EXPECT_THAT(surface->attrib(mir_surface_attrib_state),
701- Eq(mir_surface_state_unknown));
702-}
703-
704 TEST_F(MirClientSurfaceTest, get_cpu_region_returns_correct_data)
705 {
706 using namespace testing;
707
708=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
709--- tests/unit-tests/scene/test_basic_surface.cpp 2014-07-09 07:54:29 +0000
710+++ tests/unit-tests/scene/test_basic_surface.cpp 2014-07-09 21:43:55 +0000
711@@ -468,48 +468,150 @@
712 EXPECT_EQ(mi::InputReceptionMode::receives_all_input, surface.reception_mode());
713 }
714
715-TEST_F(BasicSurfaceTest, notifies_about_visibility_attrib_changes)
716+namespace
717+{
718+
719+struct AttributeTestParameters
720+{
721+ MirSurfaceAttrib attribute;
722+ int default_value;
723+ int a_valid_value;
724+ int an_invalid_value;
725+};
726+
727+struct BasicSurfaceAttributeTest : public BasicSurfaceTest,
728+ public ::testing::WithParamInterface<AttributeTestParameters>
729+{
730+};
731+
732+AttributeTestParameters const surface_visibility_test_parameters{
733+ mir_surface_attrib_visibility,
734+ mir_surface_visibility_exposed,
735+ mir_surface_visibility_occluded,
736+ -1
737+};
738+
739+AttributeTestParameters const surface_type_test_parameters{
740+ mir_surface_attrib_type,
741+ mir_surface_type_normal,
742+ mir_surface_type_freestyle,
743+ -1
744+};
745+
746+AttributeTestParameters const surface_state_test_parameters{
747+ mir_surface_attrib_state,
748+ mir_surface_state_restored,
749+ mir_surface_state_fullscreen,
750+ 1178312
751+};
752+
753+AttributeTestParameters const surface_swapinterval_test_parameters{
754+ mir_surface_attrib_swapinterval,
755+ 1,
756+ 0,
757+ -1
758+};
759+
760+AttributeTestParameters const surface_dpi_test_parameters{
761+ mir_surface_attrib_dpi,
762+ 0,
763+ 90,
764+ -1
765+};
766+
767+AttributeTestParameters const surface_focus_test_parameters{
768+ mir_surface_attrib_focus,
769+ mir_surface_unfocused,
770+ mir_surface_focused,
771+ -1
772+};
773+
774+}
775+
776+TEST_P(BasicSurfaceAttributeTest, default_value)
777+{
778+ auto const& params = GetParam();
779+ auto const& attribute = params.attribute;
780+ auto const& default_value = params.default_value;
781+
782+ EXPECT_EQ(default_value, surface.query(attribute));
783+}
784+
785+TEST_P(BasicSurfaceAttributeTest, notifies_about_attrib_changes)
786 {
787 using namespace testing;
788+
789+ auto const& params = GetParam();
790+ auto const& attribute = params.attribute;
791+ auto const& value1 = params.a_valid_value;
792+ auto const& value2 = params.default_value;
793+
794 MockSurfaceAttribObserver mock_surface_observer;
795
796 InSequence s;
797- EXPECT_CALL(mock_surface_observer, attrib_changed(mir_surface_attrib_visibility, mir_surface_visibility_occluded))
798+ EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, value1))
799 .Times(1);
800- EXPECT_CALL(mock_surface_observer, attrib_changed(mir_surface_attrib_visibility, mir_surface_visibility_exposed))
801+ EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, value2))
802 .Times(1);
803
804 surface.add_observer(mt::fake_shared(mock_surface_observer));
805
806- surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded);
807- surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed);
808+ surface.configure(attribute, value1);
809+ surface.configure(attribute, value2);
810 }
811
812-TEST_F(BasicSurfaceTest, does_not_notify_if_visibility_attrib_is_unchanged)
813+TEST_P(BasicSurfaceAttributeTest, does_not_notify_if_attrib_is_unchanged)
814 {
815 using namespace testing;
816+
817+ auto const& params = GetParam();
818+ auto const& attribute = params.attribute;
819+ auto const& default_value = params.default_value;
820+ auto const& another_value = params.a_valid_value;
821+
822 MockSurfaceAttribObserver mock_surface_observer;
823
824- EXPECT_CALL(mock_surface_observer, attrib_changed(mir_surface_attrib_visibility, mir_surface_visibility_occluded))
825+ EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, another_value))
826 .Times(1);
827
828 surface.add_observer(mt::fake_shared(mock_surface_observer));
829
830- surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed);
831- surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded);
832- surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded);
833+ surface.configure(attribute, default_value);
834+ surface.configure(attribute, another_value);
835+ surface.configure(attribute, another_value);
836 }
837
838-TEST_F(BasicSurfaceTest, throws_on_invalid_visibility_attrib_value)
839+TEST_P(BasicSurfaceAttributeTest, throws_on_invalid_value)
840 {
841 using namespace testing;
842-
843+
844+ auto const& params = GetParam();
845+ auto const& attribute = params.attribute;
846+ auto const& invalid_value = params.an_invalid_value;
847+
848 EXPECT_THROW({
849- surface.configure(mir_surface_attrib_visibility,
850- static_cast<int>(mir_surface_visibility_exposed) + 1);
851- }, std::logic_error);
852+ surface.configure(attribute, invalid_value);
853+ }, std::logic_error);
854 }
855
856+INSTANTIATE_TEST_CASE_P(SurfaceTypeAttributeTest, BasicSurfaceAttributeTest,
857+ ::testing::Values(surface_type_test_parameters));
858+
859+INSTANTIATE_TEST_CASE_P(SurfaceVisibilityAttributeTest, BasicSurfaceAttributeTest,
860+ ::testing::Values(surface_visibility_test_parameters));
861+
862+INSTANTIATE_TEST_CASE_P(SurfaceStateAttributeTest, BasicSurfaceAttributeTest,
863+ ::testing::Values(surface_state_test_parameters));
864+
865+INSTANTIATE_TEST_CASE_P(SurfaceSwapintervalAttributeTest, BasicSurfaceAttributeTest,
866+ ::testing::Values(surface_swapinterval_test_parameters));
867+
868+INSTANTIATE_TEST_CASE_P(SurfaceDPIAttributeTest, BasicSurfaceAttributeTest,
869+ ::testing::Values(surface_dpi_test_parameters));
870+
871+INSTANTIATE_TEST_CASE_P(SurfaceFocusAttributeTest, BasicSurfaceAttributeTest,
872+ ::testing::Values(surface_focus_test_parameters));
873+
874 TEST_F(BasicSurfaceTest, configure_returns_value_set_by_configurator)
875 {
876 using namespace testing;
877
878=== modified file 'tests/unit-tests/scene/test_surface_impl.cpp'
879--- tests/unit-tests/scene/test_surface_impl.cpp 2014-06-23 22:25:17 +0000
880+++ tests/unit-tests/scene/test_surface_impl.cpp 2014-07-09 21:43:55 +0000
881@@ -186,52 +186,6 @@
882 EXPECT_EQ(mir_surface_state_fullscreen, surf.state());
883 }
884
885-TEST_F(Surface, dpi_is_initialized)
886-{
887- using namespace testing;
888-
889- ms::BasicSurface surf(
890- std::string("stub"),
891- geom::Rectangle{{},{}},
892- false,
893- buffer_stream,
894- std::shared_ptr<mi::InputChannel>(),
895- stub_input_sender,
896- null_configurator,
897- std::shared_ptr<mg::CursorImage>(),
898- report);
899-
900- EXPECT_EQ(0, surf.dpi()); // The current default. It will change.
901-}
902-
903-TEST_F(Surface, dpi_changes)
904-{
905- using namespace testing;
906-
907- ms::BasicSurface surf(
908- std::string("stub"),
909- geom::Rectangle{{},{}},
910- false,
911- buffer_stream,
912- std::shared_ptr<mi::InputChannel>(),
913- stub_input_sender,
914- null_configurator,
915- std::shared_ptr<mg::CursorImage>(),
916- report);
917-
918- EXPECT_EQ(123, surf.configure(mir_surface_attrib_dpi, 123));
919- EXPECT_EQ(123, surf.dpi());
920-
921- EXPECT_EQ(456, surf.configure(mir_surface_attrib_dpi, 456));
922- EXPECT_EQ(456, surf.dpi());
923-
924- surf.configure(mir_surface_attrib_dpi, -1);
925- EXPECT_EQ(456, surf.dpi());
926-
927- EXPECT_EQ(789, surf.configure(mir_surface_attrib_dpi, 789));
928- EXPECT_EQ(789, surf.dpi());
929-}
930-
931 bool operator==(MirEvent const& a, MirEvent const& b)
932 {
933 // We will always fill unused bytes with zero, so memcmp is accurate...

Subscribers

People subscribed via source and target branches