[Linux] Unify Editor and Game raw mouse event handling

Signed-off-by: Chris Burel <burelc@amazon.com>
monroegm-disable-blank-issue-2
Chris Burel 4 years ago
parent d598a7c709
commit ae1b6d6729

@ -10,6 +10,8 @@
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbConnectionManager.h>
#include <qpa/qplatformnativeinterface.h>
#endif
namespace Editor
@ -23,16 +25,34 @@ namespace Editor
return nullptr;
}
xcb_connection_t* EditorQtApplicationXcb::GetXcbConnectionFromQt()
{
QPlatformNativeInterface* native = platformNativeInterface();
AZ_Warning("EditorQtApplicationXcb", native, "Unable to retrieve the native platform interface");
if (!native)
{
return nullptr;
}
return reinterpret_cast<xcb_connection_t*>(native->nativeResourceForIntegration(QByteArray("connection")));
}
void EditorQtApplicationXcb::OnStartPlayInEditor()
{
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
interface->SetEnableXInput(GetXcbConnectionFromQt(), true);
}
void EditorQtApplicationXcb::OnStopPlayInEditor()
{
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
interface->SetEnableXInput(GetXcbConnectionFromQt(), false);
}
bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
{
if (GetIEditor()->IsInGameMode())
{
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
// We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode.
// TODO To have this call here might be not be perfect.
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents);
// Now handle the rest of the events.
AzFramework::XcbEventHandlerBus::Broadcast(
&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
#endif

@ -6,19 +6,35 @@
*
*/
#if !defined(Q_MOC_RUN)
#include <Editor/Core/QtEditorApplication.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#endif
using xcb_connection_t = struct xcb_connection_t;
namespace Editor
{
class EditorQtApplicationXcb : public EditorQtApplication
class EditorQtApplicationXcb
: public EditorQtApplication
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
{
Q_OBJECT
public:
EditorQtApplicationXcb(int& argc, char** argv)
: EditorQtApplication(argc, argv)
{
// Connect bus to listen for OnStart/StopPlayInEditor events
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
}
xcb_connection_t* GetXcbConnectionFromQt();
///////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorEntityContextNotificationBus overrides
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
// QAbstractNativeEventFilter:
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
};

@ -10,6 +10,8 @@
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbInterface.h>
#include <xcb/xinput.h>
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
@ -34,6 +36,31 @@ namespace AzFramework
return m_xcbConnection.get();
}
void SetEnableXInput(xcb_connection_t* connection, bool enable) override
{
struct Mask
{
xcb_input_event_mask_t head;
xcb_input_xi_event_mask_t mask;
};
const Mask mask {
/*.head=*/{
/*.device_id=*/XCB_INPUT_DEVICE_ALL_MASTER,
/*.mask_len=*/1
},
/*.mask=*/ enable ?
(xcb_input_xi_event_mask_t)(XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE) :
(xcb_input_xi_event_mask_t)XCB_NONE
};
const xcb_setup_t* xcbSetup = xcb_get_setup(connection);
const xcb_screen_t* xcbScreen = xcb_setup_roots_iterator(xcbSetup).data;
xcb_input_xi_select_events(connection, xcbScreen->root, 1, &mask.head);
xcb_flush(connection);
}
private:
XcbUniquePtr<xcb_connection_t, xcb_disconnect> m_xcbConnection = nullptr;
};

@ -24,6 +24,9 @@ namespace AzFramework
virtual ~XcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
//! Enables/Disables XInput Raw Input events.
virtual void SetEnableXInput(xcb_connection_t* connection, bool enable) = 0;
};
class XcbConnectionManagerBusTraits

@ -23,9 +23,6 @@ namespace AzFramework
virtual ~XcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
// ATTN This is used as a workaround for RAW Input events when using the Editor.
virtual void PollSpecialEvents(){};
};
class XcbEventHandlerBusTraits : public AZ::EBusTraits

@ -64,7 +64,7 @@ namespace AzFramework
return nullptr;
}
s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
s_xcbConnection = interface->GetXcbConnection();
if (!s_xcbConnection)
{
AZ_Warning("XcbInput", false, "XCB connection not available");
@ -268,33 +268,6 @@ namespace AzFramework
return m_xInputInitialized;
}
void XcbInputDeviceMouse::SetEnableXInput(bool enable)
{
struct
{
xcb_input_event_mask_t head;
int mask;
} mask;
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
mask.head.mask_len = 1;
if (enable)
{
mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
}
else
{
mask.mask = XCB_NONE;
}
xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head);
xcb_flush(s_xcbConnection);
}
void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState)
{
if (systemCursorState != m_systemCursorState)
@ -354,8 +327,6 @@ namespace AzFramework
m_prevConstraintWindow = window;
}
SetEnableXInput(!cursorShown);
CreateBarriers(window, confined);
ShowCursor(window, cursorShown);
}
@ -500,14 +471,6 @@ namespace AzFramework
}
}
void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event)
{
const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_motion_event_t*>(event);
m_systemCursorPosition[0] = mouseMotionEvent->event_x;
m_systemCursorPosition[1] = mouseMotionEvent->event_y;
}
void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event)
{
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
@ -552,78 +515,20 @@ namespace AzFramework
}
}
void XcbInputDeviceMouse::PollSpecialEvents()
{
while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection))
{
// TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events.
switch (genericEvent->response_type & ~0x80)
{
case XCB_GE_GENERIC:
{
const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(genericEvent);
// Only handle raw inputs if we have focus.
// Handle Raw Input events first.
if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
(geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) ||
(geGenericEvent->event_type == XCB_INPUT_RAW_MOTION))
{
HandleRawInputEvents(geGenericEvent);
free(genericEvent);
}
}
break;
}
}
}
void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event)
{
switch (event->response_type & ~0x80)
{
// QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events.
// XInput raw events are sent from the server as a XCB_GE_GENERIC
// event. A XCB_GE_GENERIC event is typecast to a
// xcb_ge_generic_event_t, which is distinct from a
// xcb_generic_event_t, and exists so that X11 extensions can extend
// the event emission beyond the size that a normal X11 event could
// contain.
case XCB_GE_GENERIC:
{
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
// Handling RAW Inputs here works in GameMode but not in Editor mode because QT is
// not handling RAW input events and passing to.
if (!m_cursorShown)
{
// Handle Raw Input events first.
if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
(genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION))
{
HandleRawInputEvents(genericEvent);
}
}
else
{
switch (genericEvent->event_type)
{
case XCB_INPUT_BUTTON_PRESS:
{
const xcb_input_button_press_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_button_press_event_t*>(genericEvent);
HandleButtonPressEvents(mouseButtonEvent->detail, true);
}
break;
case XCB_INPUT_BUTTON_RELEASE:
{
const xcb_input_button_release_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_button_release_event_t*>(genericEvent);
HandleButtonPressEvents(mouseButtonEvent->detail, false);
}
break;
case XCB_INPUT_MOTION:
{
HandlePointerMotionEvents(event);
}
break;
}
}
HandleRawInputEvents(genericEvent);
}
break;
case XCB_FOCUS_IN:
@ -634,6 +539,9 @@ namespace AzFramework
m_focusWindow = focusInEvent->event;
HandleCursorState(m_focusWindow, m_systemCursorState);
}
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
interface->SetEnableXInput(interface->GetXcbConnection(), true);
}
break;
case XCB_FOCUS_OUT:
@ -645,6 +553,9 @@ namespace AzFramework
ResetInputChannelStates();
m_focusWindow = XCB_NONE;
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
interface->SetEnableXInput(interface->GetXcbConnection(), false);
}
break;
}

@ -65,9 +65,6 @@ namespace AzFramework
//! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice
void TickInputDevice() override;
//! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode.
void PollSpecialEvents() override;
//! Handle X11 events.
void HandleXcbEvent(xcb_generic_event_t* event) override;
@ -77,9 +74,6 @@ namespace AzFramework
//! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor.
static bool InitializeXInput();
//! Enables/Disables XInput Raw Input events.
void SetEnableXInput(bool enable);
//! Create barriers.
void CreateBarriers(xcb_window_t window, bool create);
@ -98,9 +92,6 @@ namespace AzFramework
//! Handle button press/release events.
void HandleButtonPressEvents(uint32_t detail, bool pressed);
//! Handle motion notify events.
void HandlePointerMotionEvents(const xcb_generic_event_t* event);
//! Will set cursor states and confinement modes.
void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState);
@ -160,7 +151,6 @@ namespace AzFramework
AZ::Vector2 m_cursorHiddenPosition;
AZ::Vector2 m_systemCursorPositionNormalized;
uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS];
static xcb_connection_t* s_xcbConnection;
static xcb_screen_t* s_xcbScreen;

Loading…
Cancel
Save