| /* |
| * Copyright © 2018 Canonical Ltd. |
| * |
| * This program is free software: you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 3, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * Authored by: William Wold <william.wold@canonical.com> |
| */ |
| |
| #include "helpers.h" |
| #include "in_process_server.h" |
| #include "surface_builder.h" |
| #include "input_method.h" |
| |
| #include <gmock/gmock.h> |
| |
| #include <vector> |
| #include <memory> |
| #include <experimental/optional> |
| |
| using namespace testing; |
| |
| enum class RegionAction |
| { |
| add, |
| subtract, |
| }; |
| |
| struct Region |
| { |
| struct Element |
| { |
| RegionAction action; |
| std::pair<int, int> top_left; |
| std::pair<int, int> size; |
| }; |
| |
| std::string name; |
| std::pair<int, int> surface_size; |
| std::vector<Element> elements; |
| |
| void apply_to_surface(wlcs::Client& client, wl_surface* surface) const |
| { |
| if (elements.empty()) |
| return; |
| auto const wl_region = wl_compositor_create_region(client.compositor()); |
| for (auto const& e: elements) |
| { |
| switch(e.action) |
| { |
| case RegionAction::add: |
| wl_region_add(wl_region, e.top_left.first, e.top_left.second, e.size.first, e.size.second); |
| break; |
| case RegionAction::subtract: |
| wl_region_subtract(wl_region, e.top_left.first, e.top_left.second, e.size.first, e.size.second); |
| break; |
| } |
| } |
| wl_surface_set_input_region(surface, wl_region); |
| wl_region_destroy(wl_region); |
| wl_surface_commit(surface); |
| client.roundtrip(); |
| } |
| }; |
| |
| struct RegionWithTestPoints |
| { |
| RegionWithTestPoints() |
| : name{"default constructed"}, |
| region{"default constructed", {0, 0}, {}}, |
| on_surface{0, 0}, |
| off_surface{0, 0} |
| { |
| } |
| |
| RegionWithTestPoints( |
| std::string const& name, |
| Region region, |
| std::pair<int, int> on_surface, |
| std::pair<int, int> delta) |
| : name{name}, |
| region{region}, |
| on_surface{on_surface}, |
| off_surface{on_surface.first + delta.first, on_surface.second + delta.second} |
| { |
| } |
| |
| std::string name; |
| Region region; |
| std::pair<int, int> on_surface; |
| std::pair<int, int> off_surface; |
| }; |
| |
| std::ostream& operator<<(std::ostream& out, RegionWithTestPoints const& param) |
| { |
| return out << param.region.name << " " << param.name; |
| } |
| |
| auto const all_surface_types = ValuesIn(wlcs::SurfaceBuilder::all_surface_types()); |
| auto const toplevel_surface_types = ValuesIn(wlcs::SurfaceBuilder::toplevel_surface_types()); |
| auto const xdg_stable_surface_type = Values( |
| std::static_pointer_cast<wlcs::SurfaceBuilder>(std::make_shared<wlcs::XdgStableSurfaceBuilder>(0, 0, 0, 0))); |
| |
| auto const all_input_types = ValuesIn(wlcs::InputMethod::all_input_methods()); |
| |
| class RegionSurfaceInputCombinations : |
| public wlcs::InProcessServer, |
| public testing::WithParamInterface<std::tuple< |
| RegionWithTestPoints, |
| std::shared_ptr<wlcs::SurfaceBuilder>, |
| std::shared_ptr<wlcs::InputMethod>>> |
| { |
| }; |
| |
| auto const surface_size{std::make_pair(215, 108)}; |
| |
| Region const default_region{"default", surface_size, {}}; |
| |
| auto const default_edges = Values( |
| RegionWithTestPoints{"left edge", default_region, |
| {0, surface_size.second / 2}, |
| {-1, 0}}, |
| RegionWithTestPoints{"bottom edge", default_region, |
| {surface_size.first / 2, surface_size.second - 1}, |
| {0, 1}}, |
| RegionWithTestPoints{"right edge", default_region, |
| {surface_size.first - 1, surface_size.second / 2}, |
| {1, 0}}, |
| RegionWithTestPoints{"top edge", default_region, |
| {surface_size.first / 2, 0}, |
| {0, -1}}); |
| |
| Region const full_surface_region{"explicitly specified full surface", surface_size, { |
| {RegionAction::add, {0, 0}, surface_size}}}; |
| |
| auto const full_surface_edges = Values( |
| RegionWithTestPoints{"left edge", full_surface_region, |
| {0, surface_size.second / 2}, |
| {-1, 0}}, |
| RegionWithTestPoints{"bottom edge", full_surface_region, |
| {surface_size.first / 2, surface_size.second - 1}, |
| {0, 1}}, |
| RegionWithTestPoints{"right edge", full_surface_region, |
| {surface_size.first - 1, surface_size.second / 2}, |
| {1, 0}}, |
| RegionWithTestPoints{"top edge", full_surface_region, |
| {surface_size.first / 2, 0}, |
| {0, -1}}); |
| |
| auto const region_inset = std::make_pair(12, 17); |
| |
| Region const smaller_region{"smaller", surface_size, { |
| {RegionAction::add, region_inset, { |
| surface_size.first - region_inset.first * 2, |
| surface_size.second - region_inset.second * 2}}}}; |
| |
| auto const smaller_region_edges = Values( |
| RegionWithTestPoints{"left edge", smaller_region, |
| {region_inset.first, surface_size.second / 2}, |
| {-1, 0}}, |
| RegionWithTestPoints{"bottom edge", smaller_region, |
| {surface_size.first / 2, surface_size.second - region_inset.second - 1}, |
| {0, 1}}, |
| RegionWithTestPoints{"right edge", smaller_region, |
| {surface_size.first - region_inset.first - 1, surface_size.second / 2}, |
| {1, 0}}, |
| RegionWithTestPoints{"top edge", smaller_region, |
| {surface_size.first / 2, region_inset.second}, |
| {0, -1}}); |
| |
| // If a region is larger then the surface it should be clipped |
| |
| auto const region_outset = std::make_pair(12, 17); |
| |
| Region const larger_region{"larger", surface_size, { |
| {RegionAction::add, {-region_outset.first, -region_outset.second}, { |
| surface_size.first + region_inset.first * 2, |
| surface_size.second + region_inset.second * 2}}}}; |
| |
| auto const larger_region_edges = Values( |
| RegionWithTestPoints{"left edge", larger_region, |
| {0, surface_size.second / 2}, |
| {-1, 0}}, |
| RegionWithTestPoints{"bottom edge", larger_region, |
| {surface_size.first / 2, surface_size.second - 1}, |
| {0, 1}}, |
| RegionWithTestPoints{"right edge", larger_region, |
| {surface_size.first - 1, surface_size.second / 2}, |
| {1, 0}}, |
| RegionWithTestPoints{"top edge", larger_region, |
| {surface_size.first / 2, 0}, |
| {0, -1}}); |
| |
| int const small_rect_inset = 16; |
| |
| // Looks something like this: |
| // (dotted line is real surface, solid line is input region rectangles) |
| // _______A_______ |
| // | | |
| // B| |C |
| // |_D___________E_| |
| // : | | : |
| // : F| |G : |
| // '---|---H---|---' |
| // |_______| |
| // I |
| Region const multi_rect_region{"multi-rect", surface_size, { |
| // upper rect |
| {RegionAction::add, |
| {0, 0}, |
| {surface_size.first, surface_size.second / 2}}, |
| // lower rect |
| {RegionAction::add, |
| {small_rect_inset, surface_size.second / 2}, |
| {surface_size.first - small_rect_inset * 2, surface_size.second / 2 + 20}}}}; |
| |
| auto const multi_rect_edges = Values( |
| RegionWithTestPoints{"top region edge at surface top edge", multi_rect_region, // A in diagram |
| {surface_size.first / 2, 0}, |
| {0, -1}}, |
| RegionWithTestPoints{"right region edge at surface right edge", multi_rect_region, // C in diagram |
| {surface_size.first - 1, surface_size.second / 4}, |
| {1, 0}}, |
| RegionWithTestPoints{"left region edge inside surface", multi_rect_region, // F in diagram |
| {small_rect_inset, surface_size.second * 3 / 4}, |
| {-1, 0}}, |
| RegionWithTestPoints{"step edge", multi_rect_region, // D in diagram |
| {small_rect_inset / 2, surface_size.second / 2 - 1}, |
| {0, 1}}, |
| RegionWithTestPoints{"bottom clipped edge", multi_rect_region, // I in diagram |
| {surface_size.first / 2, surface_size.second - 1}, |
| {0, 1}}); |
| |
| auto const multi_rect_corners = Values( |
| RegionWithTestPoints{ |
| "top-left corner", multi_rect_region, // AxB in diagram |
| {0, 0}, |
| {-1, -1}}, |
| RegionWithTestPoints{ |
| "top-right corner", multi_rect_region, // AxC in diagram |
| {surface_size.first - 1, 0}, |
| {1, -1}}, |
| RegionWithTestPoints{ |
| "bottom-left corner", multi_rect_region, // HxF in diagram |
| {small_rect_inset, surface_size.second - 1}, |
| {-1, 1}}, |
| RegionWithTestPoints{ |
| "bottom-right corner", multi_rect_region, // HxG in diagram |
| {surface_size.first - small_rect_inset - 1, surface_size.second - 1}, |
| {1, 1}}, |
| RegionWithTestPoints{ |
| "left interior corner", multi_rect_region, // HxF in diagram |
| {small_rect_inset, surface_size.second / 2 - 1}, |
| {-1, 1}}, |
| RegionWithTestPoints{ |
| "right interior corner", multi_rect_region, // HxG in diagram |
| {surface_size.first - small_rect_inset - 1, surface_size.second / 2 - 1}, |
| {1, 1}}); |
| |
| // TODO: test subtract |
| // TODO: test empty region |
| // TODO: test default region |
| |
| TEST_P(RegionSurfaceInputCombinations, input_inside_region_seen) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| RegionWithTestPoints region; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| |
| std::tie(region, builder, input) = GetParam(); |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| region.region.surface_size); |
| region.region.apply_to_surface(client, *surface); |
| struct wl_surface* const wl_surface = *surface; |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({ |
| top_left.first + region.on_surface.first, |
| top_left.second + region.on_surface.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Eq(wl_surface)) |
| << input << " not seen by "<< builder << " when inside " << region.name << " of " << region.region.name << " region"; |
| |
| if (input->current_surface(client) == wl_surface) |
| { |
| EXPECT_THAT(input->position_on_surface(client), Eq(region.on_surface)) |
| << input << " in the wrong place over " << builder << " while testing " << region; |
| } |
| } |
| |
| TEST_P(RegionSurfaceInputCombinations, input_not_seen_after_leaving_region) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| RegionWithTestPoints region; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(region, builder, input) = GetParam(); |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| region.region.surface_size); |
| region.region.apply_to_surface(client, *surface); |
| struct wl_surface* const wl_surface = *surface; |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({ |
| top_left.first + region.on_surface.first, |
| top_left.second + region.on_surface.second}); |
| client.roundtrip(); |
| device->up(); |
| device->down_at({ |
| top_left.first + region.off_surface.first, |
| top_left.second + region.off_surface.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(wl_surface)) |
| << input << " seen by " << builder << " when outside " << region.name << " of " << region.region.name << " region"; |
| } |
| |
| class SurfaceInputCombinations : |
| public wlcs::InProcessServer, |
| public testing::WithParamInterface<std::tuple< |
| std::shared_ptr<wlcs::SurfaceBuilder>, |
| std::shared_ptr<wlcs::InputMethod>>> |
| { |
| }; |
| |
| TEST_P(SurfaceInputCombinations, input_not_seen_in_region_after_null_buffer_committed) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto const region = full_surface_region; |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| region.surface_size); |
| region.apply_to_surface(client, *surface); |
| struct wl_surface* const wl_surface = *surface; |
| wl_surface_attach(wl_surface, nullptr, 0, 0); |
| wl_surface_commit(wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at(top_left); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(wl_surface)) |
| << input << " seen by " << builder << " after null buffer committed"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_not_seen_in_surface_without_region_after_null_buffer_committed) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const wl_surface = *surface; |
| wl_surface_attach(wl_surface, nullptr, 0, 0); |
| wl_surface_commit(wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at(top_left); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(wl_surface)) |
| << input << " seen by " << builder << " after null buffer committed"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_not_seen_over_empty_region) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const wl_surface = *surface; |
| |
| auto const wl_region = wl_compositor_create_region(client.compositor()); |
| wl_surface_set_input_region(*surface, wl_region); |
| wl_region_destroy(wl_region); |
| wl_surface_commit(*surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + 4, top_left.second + 4}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(wl_surface)) |
| << input << " seen by " << builder << " with empty input region"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_hits_parent_after_falling_through_subsurface) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| auto const input_offset = std::make_pair(4, 4); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto parent = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const parent_wl_surface = *parent; |
| auto subsurface = std::make_unique<wlcs::Subsurface>(wlcs::Subsurface::create_visible( |
| *parent, |
| 0, 0, |
| surface_size.first, surface_size.second)); |
| wl_subsurface_set_desync(*subsurface); |
| struct wl_surface* const sub_wl_surface = *subsurface; |
| |
| Region const region{"region", surface_size, {{RegionAction::add, {0, 0}, {1, 1}}}}; |
| region.apply_to_surface(client, sub_wl_surface); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + input_offset.first, top_left.second + input_offset.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(sub_wl_surface)) |
| << input << " seen by subsurface when not over region"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(parent_wl_surface)) |
| << input << " not seen by " << builder << " when it should have fallen through the subsurface input region"; |
| |
| EXPECT_THAT(input->position_on_surface(client), Eq(input_offset)) |
| << input << " seen in the wrong place"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, unmapping_parent_stops_subsurface_getting_input) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto parent = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const parent_wl_surface = *parent; |
| auto subsurface = std::make_unique<wlcs::Subsurface>(wlcs::Subsurface::create_visible( |
| *parent, |
| 0, 0, |
| surface_size.first, surface_size.second)); |
| wl_subsurface_set_desync(*subsurface); |
| struct wl_surface* const sub_wl_surface = *subsurface; |
| client.roundtrip(); |
| |
| wl_surface_attach(parent_wl_surface, nullptr, 0, 0); |
| wl_surface_commit(parent_wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + 4, top_left.second + 4}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(parent_wl_surface)) |
| << input << " seen by " << builder << " after it was unmapped"; |
| |
| EXPECT_THAT(input->current_surface(client), Ne(sub_wl_surface)) |
| << input << " seen by subsurface after parent " << builder << " was unmapped"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_falls_through_subsurface_when_unmapped) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto lower = client.create_visible_surface(surface_size.first, surface_size.second); |
| the_server().move_surface_to(lower, top_left.first - 100, top_left.second); |
| struct wl_surface* const lower_wl_surface = lower; |
| |
| auto parent = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| ASSERT_THAT(surface_size.first, Gt(100)); |
| struct wl_surface* const parent_wl_surface = *parent; |
| auto subsurface = std::make_unique<wlcs::Subsurface>(wlcs::Subsurface::create_visible( |
| *parent, |
| -100, 0, |
| surface_size.first, surface_size.second)); |
| wl_subsurface_set_desync(*subsurface); |
| struct wl_surface* const sub_wl_surface = *subsurface; |
| client.roundtrip(); |
| |
| wl_surface_attach(sub_wl_surface, nullptr, 0, 0); |
| wl_surface_commit(sub_wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first - 90, top_left.second + 10}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(sub_wl_surface)) |
| << input << " seen by subsurface after it was unmapped"; |
| |
| EXPECT_THAT(input->current_surface(client), Ne(parent_wl_surface)) |
| << input << " seen by " << builder << " even through it shouldn't have been over it's input region"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(lower_wl_surface)) |
| << input << " not seen by lower surface"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_falls_through_subsurface_when_parent_unmapped) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto lower = client.create_visible_surface(surface_size.first, surface_size.second); |
| the_server().move_surface_to(lower, top_left.first - 100, top_left.second); |
| struct wl_surface* const lower_wl_surface = lower; |
| |
| auto parent = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| ASSERT_THAT(surface_size.first, Gt(100)); |
| struct wl_surface* const parent_wl_surface = *parent; |
| auto subsurface = std::make_unique<wlcs::Subsurface>(wlcs::Subsurface::create_visible( |
| *parent, |
| -100, 0, |
| surface_size.first, surface_size.second)); |
| wl_subsurface_set_desync(*subsurface); |
| struct wl_surface* const sub_wl_surface = *subsurface; |
| client.roundtrip(); |
| |
| wl_surface_attach(parent_wl_surface, nullptr, 0, 0); |
| wl_surface_commit(parent_wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first - 90, top_left.second + 10}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(sub_wl_surface)) |
| << input << " seen by subsurface after parent was unmapped"; |
| |
| EXPECT_THAT(input->current_surface(client), Ne(parent_wl_surface)) |
| << input << " seen by " << builder << " after being unmapped (also input should have gone to subsurface)"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(lower_wl_surface)) |
| << input << " not seen by lower surface"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_seen_after_surface_unmapped_and_remapped) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| auto const input_offset = std::make_pair(4, 4); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto surface = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const wl_surface = *surface; |
| |
| wl_surface_attach(wl_surface, nullptr, 0, 0); |
| wl_surface_commit(wl_surface); |
| client.roundtrip(); |
| |
| surface->attach_visible_buffer(surface_size.first, surface_size.second); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + input_offset.first, top_left.second + input_offset.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Eq(wl_surface)) |
| << input << " not seen by " << builder << " after it was unmapped and remapped"; |
| |
| EXPECT_THAT(input->position_on_surface(client), Eq(input_offset)) |
| << input << " seen in the wrong place"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_seen_by_subsurface_after_parent_unmapped_and_remapped) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| auto const input_offset = std::make_pair(-90, 10); |
| auto const subsurface_offset = std::make_pair(-100, 0); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto parent = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| ASSERT_THAT(surface_size.first, Gt(100)); |
| struct wl_surface* const parent_wl_surface = *parent; |
| auto subsurface = std::make_unique<wlcs::Subsurface>(wlcs::Subsurface::create_visible( |
| *parent, |
| subsurface_offset.first, subsurface_offset.second, |
| surface_size.first, surface_size.second)); |
| wl_subsurface_set_desync(*subsurface); |
| struct wl_surface* const sub_wl_surface = *subsurface; |
| client.roundtrip(); |
| |
| wl_surface_attach(parent_wl_surface, nullptr, 0, 0); |
| wl_surface_commit(parent_wl_surface); |
| client.roundtrip(); |
| |
| parent->attach_visible_buffer(surface_size.first, surface_size.second); |
| //the_server().move_surface_to(*parent, top_left.first, top_left.second); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + input_offset.first, top_left.second + input_offset.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(parent_wl_surface)) |
| << input << " seen by " << builder << " when it should be seen by it's subsurface"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(sub_wl_surface)) |
| << input << " not seen by subsurface after parent was unmapped and remapped"; |
| |
| EXPECT_THAT(input->position_on_surface(client), Eq( |
| std::make_pair(input_offset.first - subsurface_offset.first, input_offset.second - subsurface_offset.second))) |
| << input << " seen in the wrong place"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_seen_after_dragged_off_surface) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| auto const input_offset = std::make_pair(-5, 5); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto other = client.create_visible_surface(100, 100); |
| the_server().move_surface_to(other, top_left.first - 102, top_left.second); |
| struct wl_surface* const other_wl_surface = other; |
| |
| auto main = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const main_wl_surface = *main; |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + 5, top_left.second + 5}); |
| client.roundtrip(); |
| device->move_to({top_left.first + input_offset.first, top_left.second + input_offset.second}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(other_wl_surface)) |
| << input << " seen by second surface event though it was dragged from first"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(main_wl_surface)) |
| << input << " not seen by " << builder << " after being dragged away"; |
| |
| EXPECT_THAT(input->position_on_surface(client), Eq(input_offset)) |
| << input << " not seen by " << builder << " after being dragged away"; |
| } |
| |
| TEST_P(SurfaceInputCombinations, input_seen_by_second_surface_after_drag_off_first_and_up) |
| { |
| auto const top_left = std::make_pair(200, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto other = builder->build( |
| the_server(), |
| client, |
| std::make_pair(top_left.first - 102, top_left.second), |
| std::make_pair(100, 100)); |
| struct wl_surface* const other_wl_surface = *other; |
| |
| auto main = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const main_wl_surface = *main; |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at({top_left.first + 5, top_left.second + 5}); |
| client.roundtrip(); |
| device->move_to({top_left.first - 80, top_left.second + 5}); |
| client.roundtrip(); |
| device->up(); |
| device->down_at({top_left.first - 80, top_left.second + 5}); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(main_wl_surface)) |
| << input << " seen by first " << builder << " after being up"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(other_wl_surface)) |
| << input << " not seen by second " << builder << " after being up"; |
| } |
| |
| // Will only be instantiated with toplevel surfaces |
| class ToplevelInputCombinations : |
| public wlcs::InProcessServer, |
| public testing::WithParamInterface<std::tuple< |
| std::shared_ptr<wlcs::SurfaceBuilder>, |
| std::shared_ptr<wlcs::InputMethod>>> |
| { |
| }; |
| |
| TEST_P(ToplevelInputCombinations, input_falls_through_surface_without_region_after_null_buffer_committed) |
| { |
| auto const top_left = std::make_pair(64, 49); |
| wlcs::Client client{the_server()}; |
| std::shared_ptr<wlcs::SurfaceBuilder> builder; |
| std::shared_ptr<wlcs::InputMethod> input; |
| std::tie(builder, input) = GetParam(); |
| |
| auto lower = client.create_visible_surface(surface_size.first, surface_size.second); |
| the_server().move_surface_to(lower, top_left.first, top_left.second); |
| struct wl_surface* const lower_wl_surface = lower; |
| |
| auto upper = builder->build( |
| the_server(), |
| client, |
| top_left, |
| surface_size); |
| struct wl_surface* const upper_wl_surface = *upper; |
| wl_surface_attach(upper_wl_surface, nullptr, 0, 0); |
| wl_surface_commit(upper_wl_surface); |
| client.roundtrip(); |
| |
| auto const device = input->create_device(the_server()); |
| device->down_at(top_left); |
| client.roundtrip(); |
| |
| EXPECT_THAT(input->current_surface(client), Ne(upper_wl_surface)) |
| << input << " seen by " << builder << " after null buffer committed"; |
| |
| EXPECT_THAT(input->current_surface(client), Eq(lower_wl_surface)) |
| << input << " not seen by lower toplevel after null buffer committed to " << builder; |
| } |
| |
| // There's way too many region edges/surface type/input device combinations, so we don't run all of them |
| // multi_rect_edges covers most cases, so we test it against all surface type/input device combinations |
| // We test the rest against just XDG toplevel |
| |
| INSTANTIATE_TEST_SUITE_P( |
| MultiRectEdges, |
| RegionSurfaceInputCombinations, |
| Combine(multi_rect_edges, all_surface_types, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| DefaultEdges, |
| RegionSurfaceInputCombinations, |
| Combine(default_edges, all_surface_types, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| FullSurface, |
| RegionSurfaceInputCombinations, |
| Combine(full_surface_edges, xdg_stable_surface_type, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| SmallerRegion, |
| RegionSurfaceInputCombinations, |
| Combine(smaller_region_edges, xdg_stable_surface_type, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ClippedLargerRegion, |
| RegionSurfaceInputCombinations, |
| Combine(larger_region_edges, xdg_stable_surface_type, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| MultiRectCorners, |
| RegionSurfaceInputCombinations, |
| Combine(multi_rect_corners, xdg_stable_surface_type, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| SurfaceInputRegions, |
| SurfaceInputCombinations, |
| Combine(all_surface_types, all_input_types)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ToplevelInputRegions, |
| ToplevelInputCombinations, |
| Combine(toplevel_surface_types, all_input_types)); |