blob: d306710dad457b0813d5c2834f48b38f3d693f06 [file] [log] [blame] [edit]
/*
* Copyright © 2020 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: Alan Griffiths <alan@octopull.co.uk>
*/
#include "pointer_constraints_unstable_v1.h"
#include "helpers.h"
#include "in_process_server.h"
#include <gmock/gmock.h>
#include <memory>
using namespace testing;
using namespace wlcs;
namespace
{
auto const any_width = 300;
auto const any_height = 300;
auto const nw_middle_x = any_width / 2;
auto const nw_middle_y = any_height / 2;
auto const se_middle_x = any_width + nw_middle_x;
auto const se_middle_y = any_height + nw_middle_y;
struct PointerConstraints : StartedInProcessServer
{
// Create a client with two surface (ne & sw) and a cursor centered on the nw_surface
Client client{the_server()};
Surface se_surface{client.create_visible_surface(any_width, any_height)};
Surface nw_surface{client.create_visible_surface(any_width, any_height)};
wl_pointer* const pointer = client.the_pointer();
Pointer cursor = the_server().create_pointer();
ZwpPointerConstraintsV1 pointer_constraints{client};
std::unique_ptr<ZwpLockedPointerV1> locked_ptr = nullptr;
std::unique_ptr<ZwpConfinedPointerV1> confined_ptr = nullptr;
void SetUp() override
{
StartedInProcessServer::SetUp();
// Get the surface in a known position with the cursor over it
the_server().move_surface_to(nw_surface, 0, 0);
cursor.move_to(nw_middle_x, nw_middle_y);
the_server().move_surface_to(se_surface, any_width, any_height);
}
void TearDown() override
{
locked_ptr.reset();
confined_ptr.reset();
client.roundtrip();
StartedInProcessServer::TearDown();
}
void setup_locked_ptr_on(Surface& surface, zwp_pointer_constraints_v1_lifetime lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT)
{
locked_ptr = std::make_unique<ZwpLockedPointerV1>(pointer_constraints, surface, pointer, nullptr, lifetime);
}
void setup_confined_ptr_on(Surface& surface, zwp_pointer_constraints_v1_lifetime lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT)
{
confined_ptr = std::make_unique<ZwpConfinedPointerV1>(pointer_constraints, surface, pointer, nullptr, lifetime);
}
void setup_sync()
{
client.roundtrip();
using namespace testing;
if (locked_ptr) Mock::VerifyAndClearExpectations(locked_ptr.get());
if (confined_ptr) Mock::VerifyAndClearExpectations(confined_ptr.get());
}
void select_se_window()
{
cursor.move_to(se_middle_x, se_middle_y);
cursor.left_click();
client.roundtrip();
}
void select_nw_window()
{
cursor.move_to(nw_middle_x, nw_middle_y);
cursor.left_click();
client.roundtrip();
}
};
}
TEST_F(PointerConstraints, can_get_locked_pointer)
{
setup_locked_ptr_on(nw_surface);
EXPECT_THAT(*locked_ptr, NotNull());
}
TEST_F(PointerConstraints, locked_pointer_on_initially_focussed_surface_gets_locked_notification)
{
setup_locked_ptr_on(nw_surface);
EXPECT_CALL(*locked_ptr, locked()).Times(1);
client.roundtrip();
}
TEST_F(PointerConstraints, locked_pointer_does_not_move)
{
auto const initial_pointer_position = client.pointer_position();
setup_locked_ptr_on(nw_surface);
EXPECT_CALL(*locked_ptr, locked()).Times(AnyNumber());
setup_sync();
cursor.move_by(10, 10);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Eq(initial_pointer_position));
}
TEST_F(PointerConstraints, locked_pointer_on_initially_unfocussed_surface_gets_no_locked_notification)
{
setup_locked_ptr_on(se_surface);
EXPECT_CALL(*locked_ptr, locked()).Times(0);
client.roundtrip();
}
TEST_F(PointerConstraints, when_surface_is_selected_locked_pointer_gets_locked_notification)
{
setup_locked_ptr_on(se_surface);
setup_sync();
EXPECT_CALL(*locked_ptr, locked()).Times(1);
select_se_window();
}
TEST_F(PointerConstraints, when_surface_is_unselected_locked_pointer_gets_unlocked_notification)
{
setup_locked_ptr_on(nw_surface);
EXPECT_CALL(*locked_ptr, locked()).Times(AnyNumber());
setup_sync();
EXPECT_CALL(*locked_ptr, unlocked()).Times(1);
// A new surface will be given focus
Surface a_new_surface{client.create_visible_surface(any_width, any_height)};
client.roundtrip();
}
TEST_F(PointerConstraints, when_surface_is_reselected_persistent_locked_pointer_gets_notifications)
{
setup_locked_ptr_on(nw_surface, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
EXPECT_CALL(*locked_ptr, locked()).Times(AnyNumber());
setup_sync();
for (auto i = 3; i-- != 0;)
{
{
EXPECT_CALL(*locked_ptr, unlocked()).Times(1);
// A new surface will be given focus
Surface a_new_surface{client.create_visible_surface(any_width, any_height)};
setup_sync();
EXPECT_CALL(*locked_ptr, locked()).Times(1);
}
client.roundtrip();
}
}
TEST_F(PointerConstraints, can_get_confined_pointer)
{
setup_confined_ptr_on(nw_surface);
EXPECT_THAT(*confined_ptr, NotNull());
}
TEST_F(PointerConstraints, confined_pointer_on_initially_focussed_surface_gets_confined_notification)
{
setup_confined_ptr_on(nw_surface);
EXPECT_CALL(*confined_ptr, confined()).Times(1);
client.roundtrip();
}
TEST_F(PointerConstraints, confined_pointer_does_move)
{
auto const initial_pointer_position = client.pointer_position();
setup_confined_ptr_on(nw_surface);
EXPECT_CALL(*confined_ptr, confined()).Times(AnyNumber());
setup_sync();
cursor.move_by(10, 10);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Ne(initial_pointer_position));
}
TEST_F(PointerConstraints, confined_pointer_movement_is_constrained)
{
auto const top = wl_fixed_from_int(0);
auto const left = wl_fixed_from_int(0);
auto const bottom = wl_fixed_from_int(any_height-1);
auto const right = wl_fixed_from_int(any_width-1);
setup_confined_ptr_on(nw_surface);
EXPECT_CALL(*confined_ptr, confined()).Times(AnyNumber());
setup_sync();
cursor.move_by(2*any_width, 2*any_height);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Eq(std::make_pair(bottom, right)));
cursor.move_by(-2*any_width, -2*any_height);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Eq(std::make_pair(top, left)));
cursor.move_by(-2*any_width, 2*any_height);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Eq(std::make_pair(top, right)));
cursor.move_by(2*any_width, -2*any_height);
client.roundtrip();
EXPECT_THAT(client.pointer_position(), Eq(std::make_pair(bottom, left)));
}
TEST_F(PointerConstraints, confined_pointer_on_initially_unfocussed_surface_gets_no_confined_notification)
{
setup_confined_ptr_on(se_surface);
EXPECT_CALL(*confined_ptr, confined()).Times(0);
client.roundtrip();
}
TEST_F(PointerConstraints, when_surface_is_selected_confined_pointer_gets_confined_notification)
{
setup_confined_ptr_on(se_surface);
setup_sync();
EXPECT_CALL(*confined_ptr, confined()).Times(1);
select_se_window();
}
TEST_F(PointerConstraints, when_surface_is_unselected_confined_pointer_gets_unconfined_notification)
{
setup_confined_ptr_on(nw_surface);
EXPECT_CALL(*confined_ptr, confined()).Times(AnyNumber());
setup_sync();
EXPECT_CALL(*confined_ptr, unconfined()).Times(1);
// A new surface will be given focus
Surface a_new_surface{client.create_visible_surface(any_width, any_height)};
client.roundtrip();
}
TEST_F(PointerConstraints, when_surface_is_reselected_persistent_confined_pointer_gets_notifications)
{
setup_confined_ptr_on(nw_surface, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
EXPECT_CALL(*confined_ptr, confined()).Times(AnyNumber());
setup_sync();
for (auto i = 3; i-- != 0;)
{
{
EXPECT_CALL(*confined_ptr, unconfined()).Times(1);
// A new surface will be given focus
Surface a_new_surface{client.create_visible_surface(any_width, any_height)};
setup_sync();
EXPECT_CALL(*confined_ptr, confined()).Times(1);
}
client.roundtrip();
}
}