diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp index ad5e57479b..8ba152a9f9 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp @@ -10,6 +10,8 @@ #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB #include +#include +#include #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(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(message)); #endif diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h index 8c145c3aa7..109ae1742b 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h @@ -6,19 +6,35 @@ * */ +#if !defined(Q_MOC_RUN) #include +#include +#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; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp index f57f4a89ac..780e1e72fe 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -10,6 +10,8 @@ #include #include +#include + 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 m_xcbConnection = nullptr; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h index daa5bf35af..ca7ce06e6c 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h @@ -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 diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h index 251342093a..f32e45ed99 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -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 diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp index 56f21e6533..b938bf4825 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -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(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(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(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(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(genericEvent); - HandleButtonPressEvents(mouseButtonEvent->detail, true); - } - break; - case XCB_INPUT_BUTTON_RELEASE: - { - const xcb_input_button_release_event_t* mouseButtonEvent = - reinterpret_cast(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; } diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h index 106d204ca9..8d8f0a2845 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h @@ -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;