Merge stabilization/2110 as of 20edb35cdc into development (#5319)
This commit is contained in:
@@ -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
|
||||
|
||||
+97
-180
@@ -13,21 +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));
|
||||
}
|
||||
|
||||
// TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit
|
||||
// machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit.
|
||||
// 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.
|
||||
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
// 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;
|
||||
@@ -39,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();
|
||||
@@ -57,14 +103,14 @@ namespace AzFramework
|
||||
|
||||
InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice)
|
||||
{
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
const auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
if (!interface)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB interface not available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
|
||||
s_xcbConnection = interface->GetXcbConnection();
|
||||
if (!s_xcbConnection)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB connection not available");
|
||||
@@ -126,7 +172,7 @@ namespace AzFramework
|
||||
|
||||
// Get window information.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xcbGeometryReply)
|
||||
{
|
||||
@@ -137,7 +183,7 @@ namespace AzFramework
|
||||
xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0);
|
||||
|
||||
const XcbStdFreePtr<xcb_translate_coordinates_reply_t> xkbTranslateCoordReply{ xcb_translate_coordinates_reply(
|
||||
s_xcbConnection, translate_coord, NULL) };
|
||||
s_xcbConnection, translate_coord, nullptr) };
|
||||
|
||||
if (!xkbTranslateCoordReply)
|
||||
{
|
||||
@@ -173,11 +219,11 @@ namespace AzFramework
|
||||
for (const auto& barrier : m_activeBarriers)
|
||||
{
|
||||
xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked(
|
||||
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL);
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, nullptr);
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xcbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
AZ_Warning(
|
||||
"XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
|
||||
"XcbInput", !xcbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
|
||||
barrier.x1, barrier.y1);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +253,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
const XcbStdFreePtr<xcb_xfixes_query_version_reply_t> xkbQueryRequestReply{ xcb_xfixes_query_version_reply(
|
||||
s_xcbConnection, query_cookie, &error) };
|
||||
|
||||
@@ -244,7 +290,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
const XcbStdFreePtr<xcb_input_xi_query_version_reply_t> xkbQueryRequestReply{ xcb_input_xi_query_version_reply(
|
||||
s_xcbConnection, query_version_cookie, &error) };
|
||||
|
||||
@@ -268,40 +314,13 @@ 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)
|
||||
{
|
||||
m_systemCursorState = systemCursorState;
|
||||
|
||||
m_focusWindow = GetSystemCursorFocusWindow();
|
||||
m_focusWindow = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
|
||||
HandleCursorState(m_focusWindow, systemCursorState);
|
||||
}
|
||||
@@ -309,52 +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;
|
||||
}
|
||||
|
||||
SetEnableXInput(!cursorShown);
|
||||
const bool confined = (systemCursorState == SystemCursorState::ConstrainedAndHidden) ||
|
||||
(systemCursorState == SystemCursorState::ConstrainedAndVisible);
|
||||
const bool cursorShown = (systemCursorState == SystemCursorState::ConstrainedAndVisible) ||
|
||||
(systemCursorState == SystemCursorState::UnconstrainedAndVisible);
|
||||
|
||||
CreateBarriers(window, confined);
|
||||
ShowCursor(window, cursorShown);
|
||||
@@ -368,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(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
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;
|
||||
}
|
||||
@@ -401,7 +378,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window);
|
||||
|
||||
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) };
|
||||
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, nullptr) };
|
||||
|
||||
if (!xkbQueryPointerReply)
|
||||
{
|
||||
@@ -409,7 +386,7 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
{
|
||||
@@ -429,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();
|
||||
}
|
||||
@@ -455,11 +432,11 @@ namespace AzFramework
|
||||
cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window);
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xcbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
if (xkbError)
|
||||
if (xcbError)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code);
|
||||
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xcbError->error_code);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -500,14 +477,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 +521,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 +545,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:
|
||||
@@ -644,7 +558,10 @@ namespace AzFramework
|
||||
ProcessRawEventQueues();
|
||||
ResetInputChannelStates();
|
||||
|
||||
m_focusWindow = XCB_NONE;
|
||||
m_focusWindow = XCB_WINDOW_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;
|
||||
@@ -171,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;
|
||||
|
||||
|
||||
+74
-39
@@ -9,19 +9,71 @@
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <cerrno>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
AZ_CVAR(bool, ap_tether_lifetime, true, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"If enabled, a parent process that launches the AP will terminate the AP on exit");
|
||||
|
||||
namespace AzFramework::AssetSystem::Platform
|
||||
{
|
||||
void AllowAssetProcessorToForeground()
|
||||
{}
|
||||
|
||||
[[noreturn]] static void LaunchAssetProcessorDirectly(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath)
|
||||
{
|
||||
AZStd::fixed_vector<const char*, 5> args {
|
||||
assetProcessorPath.c_str(),
|
||||
"--start-hidden",
|
||||
};
|
||||
|
||||
// Add the engine path to the launch command if not empty
|
||||
AZ::IO::FixedMaxPathString engineRootArg;
|
||||
if (!engineRoot.empty())
|
||||
{
|
||||
// No need to quote these paths, this code calls exec directly and
|
||||
// does not go through shell string interpolation
|
||||
engineRootArg = AZ::IO::FixedMaxPathString{"--engine-path="} + AZ::IO::FixedMaxPathString{engineRoot};
|
||||
args.push_back(engineRootArg.data());
|
||||
}
|
||||
|
||||
// Add the active project path to the launch command if not empty
|
||||
AZ::IO::FixedMaxPathString projectPathArg;
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
projectPathArg = AZ::IO::FixedMaxPathString{"--regset=/Amazon/AzCore/Bootstrap/project_path="} + AZ::IO::FixedMaxPathString{projectPath};
|
||||
args.push_back(projectPathArg.data());
|
||||
}
|
||||
|
||||
// Make sure this is at the end
|
||||
args.push_back(nullptr); // argv itself needs to be null-terminated
|
||||
|
||||
execv(args[0], const_cast<char**>(args.data()));
|
||||
|
||||
// exec* family of functions only return on error
|
||||
fprintf(stderr, "Asset Processor failed with error: %s\n", strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static pid_t LaunchAssetProcessorDaemonized(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath)
|
||||
{
|
||||
// detach the child from parent
|
||||
setsid();
|
||||
const pid_t secondChildPid = fork();
|
||||
if (secondChildPid == 0)
|
||||
{
|
||||
LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath);
|
||||
}
|
||||
return secondChildPid;
|
||||
}
|
||||
|
||||
bool LaunchAssetProcessor(AZStd::string_view executableDirectory, AZStd::string_view engineRoot,
|
||||
AZStd::string_view projectPath)
|
||||
{
|
||||
@@ -40,7 +92,8 @@ namespace AzFramework::AssetSystem::Platform
|
||||
}
|
||||
}
|
||||
|
||||
pid_t firstChildPid = fork();
|
||||
const pid_t parentPid = getpid();
|
||||
const pid_t firstChildPid = fork();
|
||||
if (firstChildPid == 0)
|
||||
{
|
||||
// redirect output to dev/null so it doesn't hijack an existing console window
|
||||
@@ -53,51 +106,33 @@ namespace AzFramework::AssetSystem::Platform
|
||||
AZ::IO::FileDescriptorRedirector stderrRedirect(STDERR_FILENO);
|
||||
stderrRedirect.RedirectTo(devNull, mode);
|
||||
|
||||
// detach the child from parent
|
||||
setsid();
|
||||
pid_t secondChildPid = fork();
|
||||
if (secondChildPid == 0)
|
||||
if (ap_tether_lifetime)
|
||||
{
|
||||
AZStd::array args {
|
||||
assetProcessorPath.c_str(), assetProcessorPath.c_str(), "--start-hidden",
|
||||
static_cast<const char*>(nullptr), static_cast<const char*>(nullptr), static_cast<const char*>(nullptr)
|
||||
};
|
||||
int optionalArgPos = 3;
|
||||
|
||||
// Add the engine path to the launch command if not empty
|
||||
AZ::IO::FixedMaxPathString engineRootArg;
|
||||
if (!engineRoot.empty())
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
if (getppid() != parentPid)
|
||||
{
|
||||
engineRootArg = AZ::IO::FixedMaxPathString::format(R"(--engine-path="%.*s")",
|
||||
aznumeric_cast<int>(engineRoot.size()), engineRoot.data());
|
||||
args[optionalArgPos++] = engineRootArg.data();
|
||||
_exit(1);
|
||||
}
|
||||
LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
const pid_t secondChildPid = LaunchAssetProcessorDaemonized(assetProcessorPath, engineRoot, projectPath);
|
||||
stdoutRedirect.Reset();
|
||||
stderrRedirect.Reset();
|
||||
|
||||
// Add the active project path to the launch command if not empty
|
||||
AZ::IO::FixedMaxPathString projectPathArg;
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
projectPathArg = AZ::IO::FixedMaxPathString::format(R"(--regset="/Amazon/AzCore/Bootstrap/project_path=%.*s")",
|
||||
aznumeric_cast<int>(projectPath.size()), projectPath.data());
|
||||
args[optionalArgPos++] = projectPathArg.data();
|
||||
}
|
||||
|
||||
AZStd::apply(execl, args);
|
||||
|
||||
// exec* family of functions only exit on error
|
||||
AZ_Error("AssetSystemComponent", false, "Asset Processor failed with error: %s", strerror(errno));
|
||||
_exit(1);
|
||||
// exit the transient child with proper return code
|
||||
int ret = (secondChildPid < 0) ? 1 : 0;
|
||||
_exit(ret);
|
||||
}
|
||||
|
||||
stdoutRedirect.Reset();
|
||||
stderrRedirect.Reset();
|
||||
|
||||
// exit the transient child with proper return code
|
||||
int ret = (secondChildPid < 0) ? 1 : 0;
|
||||
_exit(ret);
|
||||
}
|
||||
else if (firstChildPid > 0)
|
||||
{
|
||||
if (ap_tether_lifetime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// wait for first child to exit to ensure the second child was started
|
||||
int status = 0;
|
||||
pid_t ret = waitpid(firstChildPid, &status, 0);
|
||||
@@ -106,4 +141,4 @@ namespace AzFramework::AssetSystem::Platform
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework::AssetSystem::Platform
|
||||
|
||||
Reference in New Issue
Block a user