Mir

Merge lp:~mir-team/mir/custom-input-regions-scale-with-surface-size into lp:mir

Proposed by Robert Carr
Status: Rejected
Rejected by: Robert Carr
Proposed branch: lp:~mir-team/mir/custom-input-regions-scale-with-surface-size
Merge into: lp:mir
Diff against target: 100 lines (+57/-2)
3 files modified
src/server/scene/basic_surface.cpp (+7/-2)
src/server/scene/basic_surface.h (+1/-0)
tests/unit-tests/scene/test_basic_surface.cpp (+49/-0)
To merge this branch: bzr merge lp:~mir-team/mir/custom-input-regions-scale-with-surface-size
Reviewer Review Type Date Requested Status
Daniel van Vugt Disapprove
Chris Halse Rogers Needs Information
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+263168@code.launchpad.net

Commit message

Scale input rectangles as the surface resizes.

Description of the change

This branch is a preparation for client specified input regions.

To refresh, the requirement is that clients can list regions of their surface which are eligible to receive input and regions which input falls through.

One important question is...what happens when the surface resizes but the client has yet to supply new input rectangles? It seems clear the input rectangles should be scaled to the new surface size...if the client wishes to increase or decrease the resolution of the input mesh a new input shape could be applied.

Recall, we already have input rectangles on the server side, though mostly in an unused format. This branch makes them scale according to the surface size when they were applied.

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

Another option is that the client is *not* resized without supplying new input rectangles, or that a resize clears any existing rectangles. The client is then required to submit new input extents before any resize is rendered.

I think that the “just scale up the rectangles” approach isn't going to be useful; that's only useful if the client responds to a resize by drawing exactly the same thing but bigger. Clients are almost always going to respond to a resize by drawing more stuff, rather than the same stuff but bigger.

Although if we want the server to be able to resize surfaces - *and* have the user interact with them - without blocking on the client this is necessary.

Hm. I think I'm a -0 on this. Do we need to do this?

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

I agree, this is not going to be useful.

What I learned very quickly when implementing resizing was that the dominant use case when resizing a surface is that the widgets within that surface mostly don't move (relative to the top left corner). So that's why you see we never scale the surface contents with resizing, but clamp-to-edge instead (stick to the left and top edges, filling the right/bottom with identical pixels till the client supplies a new buffer of the correct size).

As we don't scale our pixels during resizing I don't think it makes any sense to scale the input regions either. Correct behaviour will almost always be: Leave things exactly where they were (relative to top-left) until the client provides information to the contrary.

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

18 + auto x_scale = surface_rect.size.width.as_float() / size_when_input_rectangles_set.width.as_float();
19 + auto y_scale = surface_rect.size.height.as_float() / size_when_input_rectangles_set.height.as_float();

seems like a geom::Width / geom::Width should make a geom::Scalar (would save some line length)

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

I've been mulling over the concerns and whether this is really necessary...

I think it is true that clients will mostly respond by drawing more/different stuff rather than the same stuff but bigger. I have a suspicion that input shapes may mostly scale though, e.g. circular apps?

I've realized though that any sort of races I was worrying about with input shape not being up to date during resize are pretty marginal...you don't generally interact with a surface while it's being resized. I wonder if its necessary and worthwhile to make the input shape setting at least atomic with the buffer exchange though...

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

It's true you don't interact with a surface mid-resizing, but that's a less important point.

More importantly real apps don't scale their widgets when resized, most of the time. You can see this for yourself on a desktop -- just resize some apps. Most widgets stay still and some (right/bottom aligned) widgets will move, but none of them scale (in the vast majority of cases). And if the widgets are not scaling then the input areas should not scale.

Atomicity I think is unimportant. We still have at least a frame or two lag between the client and server when resizing a window, but not much more. That still means the client is catching up much faster than you can move your mouse to it, so those brief frames of lag during a rapid resize don't need pixel-accurate input regions.

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

It seems like these apps (most real apps I use on my desktop) don't use input regions though and examples I can think of (OSK, Circular/elliptical freestyle windows) would benefit from the region scaling.

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

^ That last paragraph is misleading. We already have the most pixel-accurate input regions without scaling them.

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

Come to think of it, the solution is simple:

1. Client-specified region is always intersected with the surface region to get the final result.
2. The default (when client hasn't specified) input region is an "infinite region". But intersected with the surface region that gives you just the surface.
3. Right answer every time.

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

+1, sounds reasonable

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/server/scene/basic_surface.cpp'
--- src/server/scene/basic_surface.cpp 2015-06-19 15:44:57 +0000
+++ src/server/scene/basic_surface.cpp 2015-06-26 20:00:08 +0000
@@ -245,7 +245,8 @@
245void ms::BasicSurface::set_input_region(std::vector<geom::Rectangle> const& input_rectangles)245void ms::BasicSurface::set_input_region(std::vector<geom::Rectangle> const& input_rectangles)
246{246{
247 std::unique_lock<std::mutex> lock(guard);247 std::unique_lock<std::mutex> lock(guard);
248 custom_input_rectangles = input_rectangles;248 custom_input_rectangles = input_rectangles;
249 size_when_input_rectangles_set = surface_rect.size;
249}250}
250251
251void ms::BasicSurface::resize(geom::Size const& desired_size)252void ms::BasicSurface::resize(geom::Size const& desired_size)
@@ -304,9 +305,13 @@
304 // TODO: Perhaps creates some issues with transformation.305 // TODO: Perhaps creates some issues with transformation.
305 auto local_point = geom::Point{0, 0} + (point-surface_rect.top_left);306 auto local_point = geom::Point{0, 0} + (point-surface_rect.top_left);
306307
308 auto x_scale = surface_rect.size.width.as_float() / size_when_input_rectangles_set.width.as_float();
309 auto y_scale = surface_rect.size.height.as_float() / size_when_input_rectangles_set.height.as_float();
310
307 for (auto const& rectangle : custom_input_rectangles)311 for (auto const& rectangle : custom_input_rectangles)
308 {312 {
309 if (rectangle.contains(local_point))313 auto scaled_point = geom::Point{local_point.x.as_float() / x_scale, local_point.y.as_float() / y_scale};
314 if (rectangle.contains(scaled_point))
310 {315 {
311 return true;316 return true;
312 }317 }
313318
=== modified file 'src/server/scene/basic_surface.h'
--- src/server/scene/basic_surface.h 2015-06-17 05:20:42 +0000
+++ src/server/scene/basic_surface.h 2015-06-26 20:00:08 +0000
@@ -166,6 +166,7 @@
166 input::InputReceptionMode input_mode;166 input::InputReceptionMode input_mode;
167 const bool nonrectangular;167 const bool nonrectangular;
168 std::vector<geometry::Rectangle> custom_input_rectangles;168 std::vector<geometry::Rectangle> custom_input_rectangles;
169 geometry::Size size_when_input_rectangles_set;
169 std::shared_ptr<compositor::BufferStream> const surface_buffer_stream;170 std::shared_ptr<compositor::BufferStream> const surface_buffer_stream;
170 std::shared_ptr<input::InputChannel> const server_input_channel;171 std::shared_ptr<input::InputChannel> const server_input_channel;
171 std::shared_ptr<input::InputSender> const input_sender;172 std::shared_ptr<input::InputSender> const input_sender;
172173
=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
--- tests/unit-tests/scene/test_basic_surface.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/scene/test_basic_surface.cpp 2015-06-26 20:00:08 +0000
@@ -488,6 +488,55 @@
488 EXPECT_FALSE(surface.input_area_contains(rect.top_left));488 EXPECT_FALSE(surface.input_area_contains(rect.top_left));
489}489}
490490
491// We create a 2x2 surface with a ((0, 0), (1, 1)) input rectangle.
492// as we stretch the surface we expect the input rectangle to still fill only the upper
493// left corner
494TEST_F(BasicSurfaceTest, input_regions_scale_with_size)
495{
496 surface.move_to({0, 0});
497 surface.resize({2, 2});
498
499 geom::Rectangle input_rect{{0, 0}, {1, 1}};
500 surface.set_input_region({input_rect});
501
502 EXPECT_TRUE(surface.input_area_contains(geom::Point{0, 0}));
503 EXPECT_FALSE(surface.input_area_contains(geom::Point{1, 1}));
504
505 surface.resize({10, 10});
506 EXPECT_TRUE(surface.input_area_contains(geom::Point{4, 4}));
507 EXPECT_FALSE(surface.input_area_contains(geom::Point{5, 5}));
508}
509
510TEST_F(BasicSurfaceTest, multiple_input_rectangles_scale_with_size)
511{
512 surface.move_to({0, 0});
513 surface.resize({10, 10});
514
515 geom::Rectangle input_rect_1 = {{0, 0}, {10, 3}};
516 geom::Rectangle input_rect_2 = {{0, 0}, {3, 10}};
517 surface.set_input_region({input_rect_1, input_rect_2});
518
519 // Diagonal
520 EXPECT_TRUE(surface.input_area_contains({0, 0}));
521 EXPECT_TRUE(surface.input_area_contains({1, 1}));
522 EXPECT_TRUE(surface.input_area_contains({2, 2}));
523 EXPECT_FALSE(surface.input_area_contains({3, 3}));
524
525 // Along the sides
526 EXPECT_TRUE(surface.input_area_contains({4, 2}));
527 EXPECT_TRUE(surface.input_area_contains({2, 4}));
528
529 surface.resize({100, 100});
530 EXPECT_TRUE(surface.input_area_contains({0, 0}));
531 EXPECT_TRUE(surface.input_area_contains({11, 11}));
532 EXPECT_TRUE(surface.input_area_contains({14, 13}));
533 EXPECT_TRUE(surface.input_area_contains({29, 27}));
534 EXPECT_FALSE(surface.input_area_contains({30, 30}));
535
536 EXPECT_TRUE(surface.input_area_contains({43, 24}));
537 EXPECT_TRUE(surface.input_area_contains({23, 46}));
538}
539
491TEST_F(BasicSurfaceTest, reception_mode_is_normal_by_default)540TEST_F(BasicSurfaceTest, reception_mode_is_normal_by_default)
492{541{
493 EXPECT_EQ(mi::InputReceptionMode::normal, surface.reception_mode());542 EXPECT_EQ(mi::InputReceptionMode::normal, surface.reception_mode());

Subscribers

People subscribed via source and target branches