[Linux] Return the active window when there's no cursor constraint window
The cursor constraint window is only set by the Editor. In the game launcher, this function was returning a null window, which makes `GetSystemCursorPositionNormalized()` unable to determine the cursor position. This in turn causes mouse input in ImGui to not work. Fixes #4722, LYN-7491 Signed-off-by: Chris Burel <burelc@amazon.com>
This commit is contained in:
+70
-61
@@ -13,18 +13,68 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
xcb_window_t GetSystemCursorFocusWindow()
|
||||
xcb_window_t GetSystemCursorFocusWindow(xcb_connection_t* connection)
|
||||
{
|
||||
void* systemCursorFocusWindow = nullptr;
|
||||
AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult(
|
||||
systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow);
|
||||
|
||||
if (!systemCursorFocusWindow)
|
||||
if (systemCursorFocusWindow)
|
||||
{
|
||||
return XCB_NONE;
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
}
|
||||
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
// EWMH-compliant window managers set the "_NET_ACTIVE_WINDOW" property
|
||||
// of the X server's root window to the currently active window. This
|
||||
// retrieves value of that property.
|
||||
|
||||
// Get the atom for the _NET_ACTIVE_WINDOW property
|
||||
constexpr int propertyNameLength = 18;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
XcbStdFreePtr<xcb_intern_atom_reply_t> activeWindowAtom {xcb_intern_atom_reply(
|
||||
connection,
|
||||
xcb_intern_atom(connection, /*only_if_exists=*/ 1, propertyNameLength, "_NET_ACTIVE_WINDOW"),
|
||||
&error
|
||||
)};
|
||||
if (!activeWindowAtom || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
// Get the root window
|
||||
const xcb_window_t rootWId = xcb_setup_roots_iterator(xcb_get_setup(connection)).data->root;
|
||||
|
||||
// Fetch the value of the root window's _NET_ACTIVE_WINDOW property
|
||||
XcbStdFreePtr<xcb_get_property_reply_t> property {xcb_get_property_reply(
|
||||
connection,
|
||||
xcb_get_property(
|
||||
/*c=*/connection,
|
||||
/*_delete=*/ 0,
|
||||
/*window=*/rootWId,
|
||||
/*property=*/activeWindowAtom->atom,
|
||||
/*type=*/XCB_ATOM_WINDOW,
|
||||
/*long_offset=*/0,
|
||||
/*long_length=*/1
|
||||
),
|
||||
&error
|
||||
)};
|
||||
|
||||
if (!property || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
return *static_cast<xcb_window_t*>(xcb_get_property_value(property.get()));
|
||||
}
|
||||
|
||||
xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr;
|
||||
@@ -36,8 +86,7 @@ namespace AzFramework
|
||||
: InputDeviceMouse::Implementation(inputDevice)
|
||||
, m_systemCursorState(SystemCursorState::Unknown)
|
||||
, m_systemCursorPositionNormalized(0.5f, 0.5f)
|
||||
, m_prevConstraintWindow(XCB_NONE)
|
||||
, m_focusWindow(XCB_NONE)
|
||||
, m_focusWindow(XCB_WINDOW_NONE)
|
||||
, m_cursorShown(true)
|
||||
{
|
||||
XcbEventHandlerBus::Handler::BusConnect();
|
||||
@@ -271,7 +320,7 @@ namespace AzFramework
|
||||
{
|
||||
m_systemCursorState = systemCursorState;
|
||||
|
||||
m_focusWindow = GetSystemCursorFocusWindow();
|
||||
m_focusWindow = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
|
||||
HandleCursorState(m_focusWindow, systemCursorState);
|
||||
}
|
||||
@@ -279,50 +328,10 @@ namespace AzFramework
|
||||
|
||||
void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState)
|
||||
{
|
||||
bool confined = false, cursorShown = true;
|
||||
switch (systemCursorState)
|
||||
{
|
||||
case SystemCursorState::ConstrainedAndHidden:
|
||||
{
|
||||
//!< Constrained to the application's main window and hidden
|
||||
confined = true;
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::ConstrainedAndVisible:
|
||||
{
|
||||
//!< Constrained to the application's main window and visible
|
||||
confined = true;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndHidden:
|
||||
{
|
||||
//!< Free to move outside the main window but hidden while inside
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndVisible:
|
||||
{
|
||||
//!< Free to move outside the application's main window and visible
|
||||
}
|
||||
case SystemCursorState::Unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE
|
||||
// We need however the window id to reset the cursor.
|
||||
if (XCB_NONE == window && (confined || cursorShown))
|
||||
{
|
||||
// Reuse the previous window to reset states.
|
||||
window = m_prevConstraintWindow;
|
||||
m_prevConstraintWindow = XCB_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remember the window we used to modify cursor and barrier states.
|
||||
m_prevConstraintWindow = window;
|
||||
}
|
||||
const bool confined = (systemCursorState == SystemCursorState::ConstrainedAndHidden) ||
|
||||
(systemCursorState == SystemCursorState::ConstrainedAndVisible);
|
||||
const bool cursorShown = (systemCursorState == SystemCursorState::ConstrainedAndVisible) ||
|
||||
(systemCursorState == SystemCursorState::UnconstrainedAndVisible);
|
||||
|
||||
CreateBarriers(window, confined);
|
||||
ShowCursor(window, cursorShown);
|
||||
@@ -336,26 +345,26 @@ namespace AzFramework
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized)
|
||||
{
|
||||
// TODO Basically not done at all. Added only the basic functions needed.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
if (!xcbGeometryReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xkbGeometryReply->width);
|
||||
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xkbGeometryReply->height);
|
||||
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xcbGeometryReply->width);
|
||||
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xcbGeometryReply->height);
|
||||
|
||||
xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y);
|
||||
xcb_warp_pointer(s_xcbConnection, XCB_WINDOW_NONE, window, 0, 0, 0, 0, x, y);
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
if (XCB_WINDOW_NONE == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -397,8 +406,8 @@ namespace AzFramework
|
||||
|
||||
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
if (XCB_WINDOW_NONE == window)
|
||||
{
|
||||
return AZ::Vector2::CreateZero();
|
||||
}
|
||||
@@ -549,7 +558,7 @@ namespace AzFramework
|
||||
ProcessRawEventQueues();
|
||||
ResetInputChannelStates();
|
||||
|
||||
m_focusWindow = XCB_NONE;
|
||||
m_focusWindow = XCB_WINDOW_NONE;
|
||||
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
interface->SetEnableXInput(interface->GetXcbConnection(), false);
|
||||
|
||||
@@ -161,9 +161,6 @@ namespace AzFramework
|
||||
//! Will be true if the xinput2 extension could be initialized.
|
||||
static bool m_xInputInitialized;
|
||||
|
||||
//! The window that had focus
|
||||
xcb_window_t m_prevConstraintWindow;
|
||||
|
||||
//! The current window that has focus
|
||||
xcb_window_t m_focusWindow;
|
||||
|
||||
|
||||
@@ -60,3 +60,10 @@ ACTION_TEMPLATE(ReturnMalloc,
|
||||
*value = T{ p0, p1, p2, p3, p4, p5 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2, p3, p4, p5, p6 };
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,37 @@ xcb_void_cookie_t xcb_warp_pointer(
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_warp_pointer(c, src_window, dst_window, src_x, src_y, src_width, src_height, dst_x, dst_y);
|
||||
}
|
||||
xcb_intern_atom_cookie_t xcb_intern_atom(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_intern_atom(c, only_if_exists, name_len, name);
|
||||
}
|
||||
xcb_intern_atom_reply_t* xcb_intern_atom_reply(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_intern_atom_reply(c, cookie, e);
|
||||
}
|
||||
xcb_get_property_cookie_t xcb_get_property(
|
||||
xcb_connection_t* c,
|
||||
uint8_t _delete,
|
||||
xcb_window_t window,
|
||||
xcb_atom_t property,
|
||||
xcb_atom_t type,
|
||||
uint32_t long_offset,
|
||||
uint32_t long_length)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property(c, _delete, window, property, type, long_offset, long_length);
|
||||
}
|
||||
xcb_get_property_reply_t* xcb_get_property_reply(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property_reply(c, cookie, e);
|
||||
}
|
||||
void* xcb_get_property_value(const xcb_get_property_reply_t* R)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property_value(R);
|
||||
}
|
||||
uint32_t xcb_generate_id(xcb_connection_t *c)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_generate_id(c);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// xcb-xkb
|
||||
@@ -181,6 +212,32 @@ xcb_void_cookie_t xcb_xfixes_hide_cursor_checked(xcb_connection_t* c, xcb_window
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_hide_cursor_checked(c, window);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_delete_pointer_barrier_checked(xcb_connection_t* c, xcb_xfixes_barrier_t barrier)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_delete_pointer_barrier_checked(c, barrier);
|
||||
}
|
||||
xcb_translate_coordinates_cookie_t xcb_translate_coordinates(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_translate_coordinates(c, src_window, dst_window, src_x, src_y);
|
||||
}
|
||||
xcb_translate_coordinates_reply_t* xcb_translate_coordinates_reply(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_translate_coordinates_reply(c, cookie, e);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_create_pointer_barrier_checked(
|
||||
xcb_connection_t* c,
|
||||
xcb_xfixes_barrier_t barrier,
|
||||
xcb_window_t window,
|
||||
uint16_t x1,
|
||||
uint16_t y1,
|
||||
uint16_t x2,
|
||||
uint16_t y2,
|
||||
uint32_t directions,
|
||||
uint16_t num_devices,
|
||||
const uint16_t* devices)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_create_pointer_barrier_checked(c, barrier, window, x1, y1, x2, y2, directions, num_devices, devices);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// xcb-xinput
|
||||
|
||||
@@ -82,6 +82,19 @@ public:
|
||||
uint16_t src_height,
|
||||
int16_t dst_x,
|
||||
int16_t dst_y));
|
||||
MOCK_CONST_METHOD4(xcb_intern_atom, xcb_intern_atom_cookie_t(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name));
|
||||
MOCK_CONST_METHOD3(xcb_intern_atom_reply, xcb_intern_atom_reply_t*(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD7(xcb_get_property, xcb_get_property_cookie_t(
|
||||
xcb_connection_t* c,
|
||||
uint8_t _delete,
|
||||
xcb_window_t window,
|
||||
xcb_atom_t property,
|
||||
xcb_atom_t type,
|
||||
uint32_t long_offset,
|
||||
uint32_t long_length));
|
||||
MOCK_CONST_METHOD3(xcb_get_property_reply, xcb_get_property_reply_t*(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD1(xcb_get_property_value, void*(const xcb_get_property_reply_t* R));
|
||||
MOCK_CONST_METHOD1(xcb_generate_id, uint32_t(xcb_connection_t *c));
|
||||
|
||||
// xcb-xkb
|
||||
MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor));
|
||||
@@ -108,6 +121,20 @@ public:
|
||||
MOCK_CONST_METHOD3(xcb_xfixes_query_version_reply, xcb_xfixes_query_version_reply_t*(xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_show_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_hide_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_delete_pointer_barrier_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_xfixes_barrier_t barrier));
|
||||
MOCK_CONST_METHOD5(xcb_translate_coordinates, xcb_translate_coordinates_cookie_t(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y));
|
||||
MOCK_CONST_METHOD3(xcb_translate_coordinates_reply, xcb_translate_coordinates_reply_t*(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD10(xcb_xfixes_create_pointer_barrier_checked, xcb_void_cookie_t(
|
||||
xcb_connection_t* c,
|
||||
xcb_xfixes_barrier_t barrier,
|
||||
xcb_window_t window,
|
||||
uint16_t x1,
|
||||
uint16_t y1,
|
||||
uint16_t x2,
|
||||
uint16_t y2,
|
||||
uint32_t directions,
|
||||
uint16_t num_devices,
|
||||
const uint16_t* devices));
|
||||
|
||||
// xcb-xinput
|
||||
MOCK_CONST_METHOD3(xcb_input_xi_query_version, xcb_input_xi_query_version_cookie_t(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version));
|
||||
|
||||
@@ -28,7 +28,10 @@ namespace AzFramework
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
using testing::Eq;
|
||||
using testing::Field;
|
||||
using testing::Return;
|
||||
using testing::StrEq;
|
||||
using testing::_;
|
||||
|
||||
XcbBaseTestFixture::SetUp();
|
||||
@@ -61,6 +64,37 @@ namespace AzFramework
|
||||
/*major_version=*/(uint16_t)2,
|
||||
/*minor_version=*/(uint16_t)2
|
||||
));
|
||||
|
||||
// Set the default focus window
|
||||
EXPECT_CALL(m_interface, xcb_intern_atom(&m_connection, 1, 18, StrEq("_NET_ACTIVE_WINDOW")))
|
||||
.WillRepeatedly(Return(xcb_intern_atom_cookie_t{/*.sequence=*/ 1}));
|
||||
ON_CALL(m_interface, xcb_intern_atom_reply(&m_connection, Field(&xcb_intern_atom_cookie_t::sequence, Eq(1)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_intern_atom_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_INTERN_ATOM,
|
||||
/*pad0=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)1,
|
||||
/*length=*/0u,
|
||||
/*xcb_atom_t=*/s_netActiveWindowAtom
|
||||
));
|
||||
ON_CALL(m_interface, xcb_get_property(&m_connection, 0, s_rootWindow, s_netActiveWindowAtom, XCB_ATOM_WINDOW, 0, 1))
|
||||
.WillByDefault(Return(xcb_get_property_cookie_t{/*.sequence=*/ s_getActiveWindowPropertySequence}));
|
||||
ON_CALL(m_interface, xcb_get_property_reply(&m_connection, Field(&xcb_get_property_cookie_t::sequence, Eq(s_getActiveWindowPropertySequence)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_get_property_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_GET_PROPERTY,
|
||||
/*format=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)s_getActiveWindowPropertySequence,
|
||||
/*length=*/0u,
|
||||
/*type=*/XCB_ATOM_WINDOW,
|
||||
/*bytes_after=*/0u,
|
||||
/*value_len=*/1u
|
||||
));
|
||||
ON_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence))))
|
||||
.WillByDefault(Return(const_cast<xcb_window_t*>(&s_nullWindow)));
|
||||
|
||||
ON_CALL(m_interface, xcb_get_geometry(&m_connection, _))
|
||||
.WillByDefault(Return(xcb_get_geometry_cookie_t{/*.sequence=*/1}));
|
||||
ON_CALL(m_interface, xcb_get_geometry_reply(&m_connection, Field(&xcb_get_geometry_cookie_t::sequence, Eq(1)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_get_geometry_reply_t>(s_defaultWindowGeometry));
|
||||
}
|
||||
|
||||
void PumpApplication()
|
||||
@@ -73,10 +107,13 @@ namespace AzFramework
|
||||
protected:
|
||||
static constexpr inline uint8_t s_xinputMajorOpcode = 131;
|
||||
static constexpr inline xcb_window_t s_rootWindow = 1;
|
||||
static constexpr inline xcb_window_t s_nullWindow = XCB_WINDOW_NONE;
|
||||
static constexpr inline xcb_input_device_id_t s_virtualCorePointerId = 2;
|
||||
static constexpr inline xcb_input_device_id_t s_physicalPointerDeviceId = 3;
|
||||
static constexpr inline uint16_t s_screenWidthInPixels = 3840;
|
||||
static constexpr inline uint16_t s_screenHeightInPixels = 2160;
|
||||
static constexpr inline uint16_t s_getActiveWindowPropertySequence = 2160;
|
||||
static constexpr inline xcb_atom_t s_netActiveWindowAtom = 1;
|
||||
static constexpr inline xcb_setup_t s_xcbSetup{
|
||||
/*.status=*/1,
|
||||
/*.pad0=*/0,
|
||||
@@ -109,6 +146,19 @@ namespace AzFramework
|
||||
/*.present=*/1,
|
||||
/*.major_opcode=*/s_xinputMajorOpcode,
|
||||
};
|
||||
static constexpr inline xcb_get_geometry_reply_t s_defaultWindowGeometry{
|
||||
/*.response_type=*/XCB_GET_GEOMETRY,
|
||||
/*.depth=*/0,
|
||||
/*.sequence=*/1,
|
||||
/*.length=*/0,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.x=*/100,
|
||||
/*.y=*/100,
|
||||
/*.width=*/100,
|
||||
/*.height=*/100,
|
||||
/*.border_width=*/3,
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
XcbTestApplication m_application{
|
||||
/*enabledGamepadsCount=*/0,
|
||||
/*keyboardEnabled=*/false,
|
||||
@@ -398,4 +448,98 @@ namespace AzFramework
|
||||
EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
}
|
||||
|
||||
struct GetCursorPositionParam
|
||||
{
|
||||
int16_t m_x;
|
||||
int16_t m_y;
|
||||
};
|
||||
|
||||
class XcbGetSystemCursorPositionTests
|
||||
: public XcbInputDeviceMouseTests
|
||||
, public testing::WithParamInterface<GetCursorPositionParam>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(XcbGetSystemCursorPositionTests, GetSystemCursorPositionNormalizedReturnsCorrectValue)
|
||||
{
|
||||
using testing::Eq;
|
||||
using testing::Field;
|
||||
using testing::Return;
|
||||
using testing::_;
|
||||
|
||||
xcb_window_t focusWindow = 42;
|
||||
const xcb_query_pointer_reply_t queryPointerReply{
|
||||
/*.response_type=*/XCB_QUERY_POINTER,
|
||||
/*.same_screen=*/1,
|
||||
/*.sequence=*/0,
|
||||
/*.length=*/1,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.child=*/focusWindow,
|
||||
/*.root_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.root_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.win_x=*/GetParam().m_x,
|
||||
/*.win_y=*/GetParam().m_y,
|
||||
/*.mask=*/{},
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
|
||||
// Querying the root window's pointer gives its absolute value
|
||||
const xcb_query_pointer_reply_t rootWindowQueryPointerReply{
|
||||
/*.response_type=*/XCB_QUERY_POINTER,
|
||||
/*.same_screen=*/1,
|
||||
/*.sequence=*/0,
|
||||
/*.length=*/1,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.child=*/s_rootWindow,
|
||||
/*.root_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.root_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.win_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.win_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.mask=*/{},
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence))))
|
||||
.WillRepeatedly(Return(&focusWindow));
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, focusWindow))
|
||||
.WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/1}));
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 1), _))
|
||||
.WillRepeatedly(ReturnMalloc<xcb_query_pointer_reply_t>(queryPointerReply));
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, s_rootWindow))
|
||||
.WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/2}));
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 2), _))
|
||||
.WillRepeatedly(ReturnMalloc<xcb_query_pointer_reply_t>(rootWindowQueryPointerReply));
|
||||
|
||||
m_application.Start();
|
||||
InputSystemCursorRequestBus::Event(
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::SetSystemCursorState,
|
||||
SystemCursorState::ConstrainedAndHidden);
|
||||
|
||||
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
|
||||
InputSystemCursorRequestBus::EventResult(
|
||||
systemCursorPositionNormalized,
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::GetSystemCursorPositionNormalized);
|
||||
|
||||
EXPECT_THAT(systemCursorPositionNormalized, ::testing::AllOf(
|
||||
testing::Property(&AZ::Vector2::GetX, testing::FloatEq(static_cast<float>(GetParam().m_x) / s_defaultWindowGeometry.width)),
|
||||
testing::Property(&AZ::Vector2::GetY, testing::FloatEq(static_cast<float>(GetParam().m_y) / s_defaultWindowGeometry.height))
|
||||
));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AllPointerPositions,
|
||||
XcbGetSystemCursorPositionTests,
|
||||
testing::Values(
|
||||
// Default mocked window geometry sets width and height to 100, all
|
||||
// parameter values should be within [0, 100)
|
||||
GetCursorPositionParam{ 50, 50 },
|
||||
GetCursorPositionParam{ 25, 25 },
|
||||
GetCursorPositionParam{ 0, 100 }
|
||||
)
|
||||
);
|
||||
} // namespace AzFramework
|
||||
|
||||
Reference in New Issue
Block a user