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
1=== modified file 'src/server/scene/basic_surface.cpp'
2--- src/server/scene/basic_surface.cpp 2015-06-19 15:44:57 +0000
3+++ src/server/scene/basic_surface.cpp 2015-06-26 20:00:08 +0000
4@@ -245,7 +245,8 @@
5 void ms::BasicSurface::set_input_region(std::vector<geom::Rectangle> const& input_rectangles)
6 {
7 std::unique_lock<std::mutex> lock(guard);
8- custom_input_rectangles = input_rectangles;
9+ custom_input_rectangles = input_rectangles;
10+ size_when_input_rectangles_set = surface_rect.size;
11 }
12
13 void ms::BasicSurface::resize(geom::Size const& desired_size)
14@@ -304,9 +305,13 @@
15 // TODO: Perhaps creates some issues with transformation.
16 auto local_point = geom::Point{0, 0} + (point-surface_rect.top_left);
17
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();
20+
21 for (auto const& rectangle : custom_input_rectangles)
22 {
23- if (rectangle.contains(local_point))
24+ auto scaled_point = geom::Point{local_point.x.as_float() / x_scale, local_point.y.as_float() / y_scale};
25+ if (rectangle.contains(scaled_point))
26 {
27 return true;
28 }
29
30=== modified file 'src/server/scene/basic_surface.h'
31--- src/server/scene/basic_surface.h 2015-06-17 05:20:42 +0000
32+++ src/server/scene/basic_surface.h 2015-06-26 20:00:08 +0000
33@@ -166,6 +166,7 @@
34 input::InputReceptionMode input_mode;
35 const bool nonrectangular;
36 std::vector<geometry::Rectangle> custom_input_rectangles;
37+ geometry::Size size_when_input_rectangles_set;
38 std::shared_ptr<compositor::BufferStream> const surface_buffer_stream;
39 std::shared_ptr<input::InputChannel> const server_input_channel;
40 std::shared_ptr<input::InputSender> const input_sender;
41
42=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
43--- tests/unit-tests/scene/test_basic_surface.cpp 2015-06-25 03:00:08 +0000
44+++ tests/unit-tests/scene/test_basic_surface.cpp 2015-06-26 20:00:08 +0000
45@@ -488,6 +488,55 @@
46 EXPECT_FALSE(surface.input_area_contains(rect.top_left));
47 }
48
49+// We create a 2x2 surface with a ((0, 0), (1, 1)) input rectangle.
50+// as we stretch the surface we expect the input rectangle to still fill only the upper
51+// left corner
52+TEST_F(BasicSurfaceTest, input_regions_scale_with_size)
53+{
54+ surface.move_to({0, 0});
55+ surface.resize({2, 2});
56+
57+ geom::Rectangle input_rect{{0, 0}, {1, 1}};
58+ surface.set_input_region({input_rect});
59+
60+ EXPECT_TRUE(surface.input_area_contains(geom::Point{0, 0}));
61+ EXPECT_FALSE(surface.input_area_contains(geom::Point{1, 1}));
62+
63+ surface.resize({10, 10});
64+ EXPECT_TRUE(surface.input_area_contains(geom::Point{4, 4}));
65+ EXPECT_FALSE(surface.input_area_contains(geom::Point{5, 5}));
66+}
67+
68+TEST_F(BasicSurfaceTest, multiple_input_rectangles_scale_with_size)
69+{
70+ surface.move_to({0, 0});
71+ surface.resize({10, 10});
72+
73+ geom::Rectangle input_rect_1 = {{0, 0}, {10, 3}};
74+ geom::Rectangle input_rect_2 = {{0, 0}, {3, 10}};
75+ surface.set_input_region({input_rect_1, input_rect_2});
76+
77+ // Diagonal
78+ EXPECT_TRUE(surface.input_area_contains({0, 0}));
79+ EXPECT_TRUE(surface.input_area_contains({1, 1}));
80+ EXPECT_TRUE(surface.input_area_contains({2, 2}));
81+ EXPECT_FALSE(surface.input_area_contains({3, 3}));
82+
83+ // Along the sides
84+ EXPECT_TRUE(surface.input_area_contains({4, 2}));
85+ EXPECT_TRUE(surface.input_area_contains({2, 4}));
86+
87+ surface.resize({100, 100});
88+ EXPECT_TRUE(surface.input_area_contains({0, 0}));
89+ EXPECT_TRUE(surface.input_area_contains({11, 11}));
90+ EXPECT_TRUE(surface.input_area_contains({14, 13}));
91+ EXPECT_TRUE(surface.input_area_contains({29, 27}));
92+ EXPECT_FALSE(surface.input_area_contains({30, 30}));
93+
94+ EXPECT_TRUE(surface.input_area_contains({43, 24}));
95+ EXPECT_TRUE(surface.input_area_contains({23, 46}));
96+}
97+
98 TEST_F(BasicSurfaceTest, reception_mode_is_normal_by_default)
99 {
100 EXPECT_EQ(mi::InputReceptionMode::normal, surface.reception_mode());

Subscribers

People subscribed via source and target branches