Browse Source

Consolidate all xcb functionality into one subdirectory

The Linux platform has multiple windowing systems. Support for xcb is
currently in progress, support for Wayland is planned in the future. The
way the current xcb support is included is by making some file with a
`_xcb` suffix, and placing `#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB` around
most of that file's contents.

With this change, all of the code in AzFramework that uses xcb directly is
inside the `Platform/Common/Xcb` subdirectory. It greatly reduces the
amount of code in compile-time `#ifdef` checks for the chosen windowing
system. It also provides a logical place to include O3DE-specific xcb
C++ wrappers and interfaces, without polluting non-xcb related code.

Signed-off-by: Chris Burel <[email protected]>
Chris Burel 4 years ago
parent
commit
f5effaabcc
19 changed files with 515 additions and 390 deletions
  1. 2 2
      Code/Editor/Core/QtEditorApplication_linux.cpp
  2. 1 1
      Code/Framework/AzFramework/CMakeLists.txt
  3. 20 25
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp
  4. 7 13
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h
  5. 42 0
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h
  6. 40 0
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h
  7. 221 243
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp
  8. 46 0
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h
  9. 38 0
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h
  10. 22 29
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp
  11. 9 10
      Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h
  12. 18 0
      Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake
  13. 0 54
      Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h
  14. 4 2
      Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp
  15. 27 0
      Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
  16. 4 3
      Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp
  17. 8 2
      Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake
  18. 1 5
      Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake
  19. 5 1
      Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp

+ 2 - 2
Code/Editor/Core/QtEditorApplication_linux.cpp

@@ -9,7 +9,7 @@
 #include "QtEditorApplication.h"
 
 #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-#include <AzFramework/API/ApplicationAPI_Linux.h>
+#include <AzFramework/XcbEventHandler.h>
 #endif
 
 namespace Editor
@@ -19,7 +19,7 @@ namespace Editor
         if (GetIEditor()->IsInGameMode())
         {
 #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-            AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
+            AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
 #endif
             return true;
         }

+ 1 - 1
Code/Framework/AzFramework/CMakeLists.txt

@@ -95,4 +95,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
 
     endif()
 
-endif()
+endif()

+ 20 - 25
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp

@@ -6,29 +6,26 @@
  *
  */
 
-#include <AzFramework/API/ApplicationAPI_Platform.h>
-#include <AzFramework/Application/Application.h>
-#include "Application_Linux_xcb.h"
+#include <AzFramework/XcbApplication.h>
+#include <AzFramework/XcbEventHandler.h>
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
 namespace AzFramework
 {
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    class LinuxXcbConnectionManagerImpl
-        : public LinuxXcbConnectionManagerBus::Handler
+    class XcbConnectionManagerImpl
+        : public XcbConnectionManagerBus::Handler
     {
     public:
-        LinuxXcbConnectionManagerImpl()
+        XcbConnectionManagerImpl()
         {
             m_xcbConnection = xcb_connect(nullptr, nullptr);
-            AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
-            LinuxXcbConnectionManagerBus::Handler::BusConnect();
+            AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
+            XcbConnectionManagerBus::Handler::BusConnect();
         }
 
-        ~LinuxXcbConnectionManagerImpl()
+        ~XcbConnectionManagerImpl() override
         {
-            LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
+            XcbConnectionManagerBus::Handler::BusDisconnect();
             xcb_disconnect(m_xcbConnection);   
         }
 
@@ -42,53 +39,51 @@ namespace AzFramework
     };
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    ApplicationLinux_xcb::ApplicationLinux_xcb()
+    XcbApplication::XcbApplication()
     {
         LinuxLifecycleEvents::Bus::Handler::BusConnect();
-        m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
-        if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
+        m_xcbConnectionManager = AZStd::make_unique<XcbConnectionManagerImpl>();
+        if (XcbConnectionManagerInterface::Get() == nullptr)
         {
-            LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
+            XcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
         }
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    ApplicationLinux_xcb::~ApplicationLinux_xcb()
+    XcbApplication::~XcbApplication()
     {
-        if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
+        if (XcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
         {
-            LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
+            XcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
         }
         m_xcbConnectionManager.reset();
         LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void ApplicationLinux_xcb::PumpSystemEventLoopOnce()
+    void XcbApplication::PumpSystemEventLoopOnce()
     {
         if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
         {
             if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
             {
-                LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
+                XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
                 free(event);
             }
         }
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty()
+    void XcbApplication::PumpSystemEventLoopUntilEmpty()
     {
         if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
         {
             while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
             {
-                LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
+                XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
                 free(event);
             }
         }
     }
 
-#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB        
-
 } // namespace AzFramework

+ 7 - 13
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h

@@ -5,27 +5,24 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+
 #pragma once
 
 #include <AzFramework/API/ApplicationAPI_Platform.h>
 #include <AzFramework/Application/Application.h>
+#include <AzFramework/XcbConnectionManager.h>
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
 namespace AzFramework
 {
-
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    class ApplicationLinux_xcb
+    class XcbApplication
         : public Application::Implementation
         , public LinuxLifecycleEvents::Bus::Handler
     {
     public:
         ////////////////////////////////////////////////////////////////////////////////////////////
-        AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0);
-        ApplicationLinux_xcb();
-        ~ApplicationLinux_xcb() override;
+        AZ_CLASS_ALLOCATOR(XcbApplication, AZ::SystemAllocator, 0);
+        XcbApplication();
+        ~XcbApplication() override;
 
         ////////////////////////////////////////////////////////////////////////////////////////////
         // Application::Implementation
@@ -33,9 +30,6 @@ namespace AzFramework
         void PumpSystemEventLoopUntilEmpty() override;
 
     private:
-        AZStd::unique_ptr<LinuxXcbConnectionManager>    m_xcbConnectionManager;
+        AZStd::unique_ptr<XcbConnectionManager> m_xcbConnectionManager;
     };
-
-#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-
 } // namespace AzFramework

+ 42 - 0
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/Interface/Interface.h>
+#include <AzCore/RTTI/RTTI.h>
+
+#include <xcb/xcb.h>
+
+namespace AzFramework
+{
+    class XcbConnectionManager
+    {
+    public:
+        AZ_RTTI(XcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
+
+        virtual ~XcbConnectionManager() = default;
+
+        virtual xcb_connection_t* GetXcbConnection() const = 0;
+    };
+
+    class XcbConnectionManagerBusTraits
+        : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using XcbConnectionManagerBus = AZ::EBus<XcbConnectionManager, XcbConnectionManagerBusTraits>;
+    using XcbConnectionManagerInterface = AZ::Interface<XcbConnectionManager>;
+} // namespace AzFramework

+ 40 - 0
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/RTTI/RTTI.h>
+
+#include <xcb/xcb.h>
+
+namespace AzFramework
+{
+    class XcbEventHandler
+    {
+    public:
+        AZ_RTTI(XcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
+
+        virtual ~XcbEventHandler() = default;
+
+        virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
+    };
+
+    class XcbEventHandlerBusTraits
+        : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using XcbEventHandlerBus = AZ::EBus<XcbEventHandler, XcbEventHandlerBusTraits>;
+} // namespace AzFramework

+ 221 - 243
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp

@@ -6,9 +6,10 @@
  *
  */
 
-#include <AzCore/std/typetraits/integral_constant.h>
-#include <AzFramework/API/ApplicationAPI_Linux.h>
 #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
+#include <AzFramework/XcbEventHandler.h>
+#include <AzFramework/XcbConnectionManager.h>
+#include <AzFramework/XcbInputDeviceKeyboard.h>
 
 #define explicit ExplicitIsACXXKeyword
 #include <xcb/xkb.h>
@@ -19,275 +20,252 @@
 
 namespace AzFramework
 {
-    class InputDeviceKeyboardXcb
-        : public InputDeviceKeyboard::Implementation
-        , public LinuxXcbEventHandlerBus::Handler
+    XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice)
+        : InputDeviceKeyboard::Implementation(inputDevice)
     {
-    public:
-        AZ_CLASS_ALLOCATOR(InputDeviceKeyboardXcb, AZ::SystemAllocator, 0);
+        XcbEventHandlerBus::Handler::BusConnect();
 
-        using InputDeviceKeyboard::Implementation::Implementation;
-        InputDeviceKeyboardXcb(InputDeviceKeyboard& inputDevice)
-            : InputDeviceKeyboard::Implementation(inputDevice)
+        auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
+        if (!interface)
         {
-            LinuxXcbEventHandlerBus::Handler::BusConnect();
-
-            auto* interface = AzFramework::LinuxXcbConnectionManagerInterface::Get();
-            if (!interface)
-            {
-                AZ_Warning("ApplicationLinux", false, "XCB interface not available");
-                return;
-            }
-
-            auto* connection = AzFramework::LinuxXcbConnectionManagerInterface::Get()->GetXcbConnection();
-            if (!connection)
-            {
-                AZ_Warning("ApplicationLinux", false, "XCB connection not available");
-                return;
-            }
-
-            AZStd::unique_ptr<xcb_xkb_use_extension_reply_t, DeleterForFreeFn<::std::free>> xkbUseExtensionReply{
-                xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
-            };
-            if (!xkbUseExtensionReply)
-            {
-                AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
-                return;
-            }
-            if (!xkbUseExtensionReply->supported)
-            {
-                AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
-                return;
-            }
-
-            m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
-
-            m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
-            m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
-            m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
-
-            m_initialized = true;
+            AZ_Warning("ApplicationLinux", false, "XCB interface not available");
+            return;
         }
 
-        bool IsConnected() const override
+        auto* connection = interface->GetXcbConnection();
+        if (!connection)
         {
-            return m_initialized;
+            AZ_Warning("ApplicationLinux", false, "XCB connection not available");
+            return;
         }
 
-        bool HasTextEntryStarted() const override
+        XcbStdFreePtr<xcb_xkb_use_extension_reply_t> xkbUseExtensionReply{
+            xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
+        };
+        if (!xkbUseExtensionReply)
         {
-            return false;
+            AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
+            return;
         }
-
-        void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override
+        if (!xkbUseExtensionReply->supported)
         {
+            AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
+            return;
         }
 
-        void TextEntryStop() override
-        {
-        }
+        m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
+
+        m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
+        m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
+        m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
 
-        void TickInputDevice() override
+        m_initialized = true;
+    }
+
+    bool XcbInputDeviceKeyboard::IsConnected() const
+    {
+        auto* connection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
+        return connection && !xcb_connection_has_error(connection);
+    }
+
+    bool XcbInputDeviceKeyboard::HasTextEntryStarted() const
+    {
+        return false;
+    }
+
+    void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options)
+    {
+    }
+
+    void XcbInputDeviceKeyboard::TextEntryStop()
+    {
+    }
+
+    void XcbInputDeviceKeyboard::TickInputDevice()
+    {
+        ProcessRawEventQueues();
+    }
+
+    void XcbInputDeviceKeyboard::HandleXcbEvent(xcb_generic_event_t* event)
+    {
+        if (!m_initialized)
         {
-            ProcessRawEventQueues();
+            return;
         }
 
-        void HandleXcbEvent(xcb_generic_event_t* event) override
+        switch (event->response_type & ~0x80)
         {
-            if (!IsConnected())
-            {
-                return;
-            }
-
-            switch (event->response_type & ~0x80)
-            {
-            case XCB_KEY_PRESS:
-            {
-                auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
+        case XCB_KEY_PRESS:
+        {
+            auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
 
-                const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
-                if (key)
-                {
-                    QueueRawKeyEvent(*key, true);
-                }
-                break;
-            }
-            case XCB_KEY_RELEASE:
+            const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
+            if (key)
             {
-                auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
-
-                const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
-                if (key)
-                {
-                    QueueRawKeyEvent(*key, false);
-                }
-                break;
-            }
+                QueueRawKeyEvent(*key, true);
             }
+            break;
         }
-
-    private:
-        [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const
+        case XCB_KEY_RELEASE:
         {
-            const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
+            auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
 
-            switch(keysym)
+            const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
+            if (key)
             {
-                case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
-                case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
-                case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
-                case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
-                case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
-                case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
-                case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
-                case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
-                case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
-                case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
-                case XKB_KEY_A:
-                case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
-                case XKB_KEY_B:
-                case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
-                case XKB_KEY_C:
-                case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
-                case XKB_KEY_D:
-                case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
-                case XKB_KEY_E:
-                case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
-                case XKB_KEY_F:
-                case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
-                case XKB_KEY_G:
-                case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
-                case XKB_KEY_H:
-                case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
-                case XKB_KEY_I:
-                case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
-                case XKB_KEY_J:
-                case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
-                case XKB_KEY_K:
-                case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
-                case XKB_KEY_L:
-                case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
-                case XKB_KEY_M:
-                case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
-                case XKB_KEY_N:
-                case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
-                case XKB_KEY_O:
-                case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
-                case XKB_KEY_P:
-                case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
-                case XKB_KEY_Q:
-                case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
-                case XKB_KEY_R:
-                case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
-                case XKB_KEY_S:
-                case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
-                case XKB_KEY_T:
-                case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
-                case XKB_KEY_U:
-                case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
-                case XKB_KEY_V:
-                case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
-                case XKB_KEY_W:
-                case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
-                case XKB_KEY_X:
-                case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
-                case XKB_KEY_Y:
-                case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
-                case XKB_KEY_Z:
-                case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
-                case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
-                case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
-                case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
-                case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
-                case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
-                case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
-                case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
-                case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
-                case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
-                case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
-                case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
-                case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
-                case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
-                case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
-                case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
-                case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
-                case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
-                case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
-                case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
-                case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
-                case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
-                case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
-                case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
-                case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
-                case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
-                case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
-                case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
-                case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
-                case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
-                case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
-                case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
-                case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
-                case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
-                case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
-                case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
-                case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
-                case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
-                case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
-                case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
-                case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
-                case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
-                case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
-                case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
-                case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
-                case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
-                case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
-                case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
-                case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
-                case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
-                case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
-                case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
-                case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
-                case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
-                case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
-                case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
-                case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
-                case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
-                case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
-                case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
-                case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
-                case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
-                case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
-                case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
-                case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
-                case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
-                case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
-                case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
-                case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
-                case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
-                case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
-                case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
-                case XKB_KEY_grave:
-                case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
-                case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
-                case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
-                case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
-                case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
-                default: return nullptr;
+                QueueRawKeyEvent(*key, false);
             }
+            break;
         }
+        }
+    }
 
-        template<auto freeFn>
-        using DeleterForFreeFn = AZStd::integral_constant<decltype(freeFn), freeFn>;
-
-        AZStd::unique_ptr<xkb_context, DeleterForFreeFn<xkb_context_unref>> m_xkbContext;
-        AZStd::unique_ptr<xkb_keymap, DeleterForFreeFn<xkb_keymap_unref>> m_xkbKeymap;
-        AZStd::unique_ptr<xkb_state, DeleterForFreeFn<xkb_state_unref>> m_xkbState;
-        int m_coreDeviceId{-1};
-        bool m_initialized{false};
-    };
-
-    InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
+    [[nodiscard]] const InputChannelId* XcbInputDeviceKeyboard::InputChannelFromKeyEvent(xcb_keycode_t code) const
     {
-        return aznew InputDeviceKeyboardXcb(inputDevice);
+        const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
+
+        switch(keysym)
+        {
+            case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
+            case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
+            case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
+            case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
+            case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
+            case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
+            case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
+            case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
+            case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
+            case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
+            case XKB_KEY_A:
+            case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
+            case XKB_KEY_B:
+            case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
+            case XKB_KEY_C:
+            case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
+            case XKB_KEY_D:
+            case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
+            case XKB_KEY_E:
+            case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
+            case XKB_KEY_F:
+            case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
+            case XKB_KEY_G:
+            case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
+            case XKB_KEY_H:
+            case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
+            case XKB_KEY_I:
+            case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
+            case XKB_KEY_J:
+            case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
+            case XKB_KEY_K:
+            case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
+            case XKB_KEY_L:
+            case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
+            case XKB_KEY_M:
+            case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
+            case XKB_KEY_N:
+            case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
+            case XKB_KEY_O:
+            case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
+            case XKB_KEY_P:
+            case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
+            case XKB_KEY_Q:
+            case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
+            case XKB_KEY_R:
+            case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
+            case XKB_KEY_S:
+            case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
+            case XKB_KEY_T:
+            case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
+            case XKB_KEY_U:
+            case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
+            case XKB_KEY_V:
+            case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
+            case XKB_KEY_W:
+            case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
+            case XKB_KEY_X:
+            case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
+            case XKB_KEY_Y:
+            case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
+            case XKB_KEY_Z:
+            case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
+            case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
+            case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
+            case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
+            case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
+            case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
+            case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
+            case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
+            case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
+            case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
+            case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
+            case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
+            case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
+            case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
+            case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
+            case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
+            case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
+            case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
+            case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
+            case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
+            case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
+            case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
+            case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
+            case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
+            case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
+            case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
+            case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
+            case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
+            case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
+            case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
+            case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
+            case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
+            case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
+            case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
+            case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
+            case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
+            case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
+            case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
+            case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
+            case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
+            case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
+            case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
+            case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
+            case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
+            case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
+            case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
+            case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
+            case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
+            case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
+            case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
+            case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
+            case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
+            case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
+            case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
+            case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
+            case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
+            case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
+            case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
+            case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
+            case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
+            case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
+            case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
+            case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
+            case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
+            case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
+            case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
+            case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
+            case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
+            case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
+            case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
+            case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
+            case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
+            case XKB_KEY_grave:
+            case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
+            case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
+            case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
+            case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
+            case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
+            default: return nullptr;
+        }
     }
 } // namespace AzFramework

+ 46 - 0
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
+#include <AzFramework/XcbEventHandler.h>
+#include <AzFramework/XcbInterface.h>
+
+#include <xcb/xcb.h>
+#include <xkbcommon/xkbcommon.h>
+
+namespace AzFramework
+{
+    class XcbInputDeviceKeyboard
+        : public InputDeviceKeyboard::Implementation
+        , public XcbEventHandlerBus::Handler
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(XcbInputDeviceKeyboard, AZ::SystemAllocator, 0);
+
+        using InputDeviceKeyboard::Implementation::Implementation;
+        XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice);
+
+        bool IsConnected() const override;
+
+        bool HasTextEntryStarted() const override;
+        void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override;
+        void TextEntryStop() override;
+        void TickInputDevice() override;
+
+        void HandleXcbEvent(xcb_generic_event_t* event) override;
+
+    private:
+        [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const;
+
+        XcbUniquePtr<xkb_context, xkb_context_unref> m_xkbContext;
+        XcbUniquePtr<xkb_keymap, xkb_keymap_unref> m_xkbKeymap;
+        XcbUniquePtr<xkb_state, xkb_state_unref> m_xkbState;
+        int m_coreDeviceId{-1};
+        bool m_initialized{false};
+    };
+} // namespace AzFramework

+ 38 - 0
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <xcb/xcb.h>
+
+#include <AzCore/std/smart_ptr/unique_ptr.h>
+
+namespace AzFramework
+{
+    // @brief Wrap a function pointer in a type
+    // This serves as a convenient way to wrap a function pointer in a given
+    // type. That type can then be used in a `unique_ptr` or `shared_ptr`.
+    // Using a type instead of a function pointer by value prevents the need to
+    // copy the pointer when copying the smart poiner.
+    template<auto Callable>
+    struct XcbDeleterFreeFunctionWrapper
+    {
+        using value_type = decltype(Callable);
+        static constexpr value_type s_value = Callable;
+        constexpr operator value_type() const noexcept
+        {
+            return s_value;
+        }
+    };
+
+    template<typename T, auto fn>
+    using XcbUniquePtr = AZStd::unique_ptr<T, XcbDeleterFreeFunctionWrapper<fn>>;
+
+    template<typename T>
+    using XcbStdFreePtr = XcbUniquePtr<T, ::free>;
+} // namespace AzFramework

+ 22 - 29
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp

@@ -6,41 +6,37 @@
  *
  */
 
-#include <AzFramework/API/ApplicationAPI_Platform.h>
 #include <AzFramework/Application/Application.h>
 #include <AzFramework/Windowing/NativeWindow.h>
-#include <xcb/xcb.h>
+#include <AzFramework/XcbNativeWindow.h>
+#include <AzFramework/XcbConnectionManager.h>
 
-#include "NativeWindow_Linux_xcb.h"
+#include <xcb/xcb.h>
 
 namespace AzFramework
 {
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-
-    [[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb";
+    [[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow";
     static constexpr uint8_t s_XcbFormatDataSize = 32;              // Format indicator for xcb for client messages
     static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4;    // The default border with in pixels if a border was specified
     static constexpr uint8_t s_XcbResponseTypeMask = 0x7f;          // Mask to extract the specific event type from an xcb event
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb() 
+    XcbNativeWindow::XcbNativeWindow() 
         : NativeWindow::Implementation()
     {
-        if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
+        if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
             xcbConnectionManager != nullptr)
         {
             m_xcbConnection = xcbConnectionManager->GetXcbConnection();
         }
-        AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
+        AZ_Error(XcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb()
-    {
-    }
+    XcbNativeWindow::~XcbNativeWindow() = default;
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title,
+    void XcbNativeWindow::InitWindow(const AZStd::string& title,
                                                 const WindowGeometry& geometry,
                                                 const WindowStyleMasks& styleMasks)
     {
@@ -98,13 +94,13 @@ namespace AzFramework
         
         xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString);
         xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr);
-        AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
+        AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
         m_xcbAtomProtocols = replyProtocol->atom;
 
         const static char* wmDeleteWindowString = "WM_DELETE_WINDOW";
         xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString);
         xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr);
-        AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
+        AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
         m_xcbAtomDeleteWindow = replyDeleteWindow->atom;
 
         xcbCheckResult = xcb_change_property_checked(m_xcbConnection, 
@@ -123,9 +119,9 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::Activate()
+    void XcbNativeWindow::Activate()
     {
-        LinuxXcbEventHandlerBus::Handler::BusConnect();
+        XcbEventHandlerBus::Handler::BusConnect();
 
         if (!m_activated) // nothing to do if window was already activated
         {
@@ -137,7 +133,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::Deactivate()
+    void XcbNativeWindow::Deactivate()
     {
         if (m_activated) // nothing to do if window was already deactivated
         {
@@ -148,17 +144,17 @@ namespace AzFramework
             xcb_unmap_window(m_xcbConnection, m_xcbWindow);
             xcb_flush(m_xcbConnection);
         }
-        LinuxXcbEventHandlerBus::Handler::BusDisconnect();
+        XcbEventHandlerBus::Handler::BusDisconnect();
     }    
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const
+    NativeWindowHandle XcbNativeWindow::GetWindowHandle() const
     {
         return reinterpret_cast<NativeWindowHandle>(m_xcbWindow);
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title)
+    void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
     {
         xcb_void_cookie_t xcbCheckResult;
         xcbCheckResult = xcb_change_property(m_xcbConnection,
@@ -173,7 +169,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize)
+    void XcbNativeWindow::ResizeClientArea(WindowSize clientAreaSize)
     {
         const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height };
 
@@ -184,7 +180,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const
+    uint32_t XcbNativeWindow::GetDisplayRefreshRate() const
     {
         // [GFX TODO][GHI - 2678]
         // Using 60 for now until proper support is added
@@ -192,7 +188,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie)
+    bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie)
     {
         bool result = true;
         if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie))
@@ -204,7 +200,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event)
+    void XcbNativeWindow::HandleXcbEvent(xcb_generic_event_t* event)
     {
         switch (event->response_type & s_XcbResponseTypeMask)
         {
@@ -233,7 +229,7 @@ namespace AzFramework
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
-    void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height)
+    void XcbNativeWindow::WindowSizeChanged(const uint32_t width, const uint32_t height)
     {
         if (m_width != width || m_height != height)
         {
@@ -246,7 +242,4 @@ namespace AzFramework
             }
         }
     }
-    
-#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-
 } // namespace AzFramework

+ 9 - 10
Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h

@@ -5,24 +5,25 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+
 #pragma once
 
-#include <AzFramework/API/ApplicationAPI_Platform.h>
 #include <AzFramework/Application/Application.h>
 #include <AzFramework/Windowing/NativeWindow.h>
+#include <AzFramework/XcbEventHandler.h>
+
 #include <xcb/xcb.h>
 
 namespace AzFramework
 {
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-    class NativeWindowImpl_Linux_xcb final
+    class XcbNativeWindow final
         : public NativeWindow::Implementation
-        , public LinuxXcbEventHandlerBus::Handler
+        , public XcbEventHandlerBus::Handler
     {
     public:
-        AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0);
-        NativeWindowImpl_Linux_xcb();
-        ~NativeWindowImpl_Linux_xcb() override;
+        AZ_CLASS_ALLOCATOR(XcbNativeWindow, AZ::SystemAllocator, 0);
+        XcbNativeWindow();
+        ~XcbNativeWindow() override;
 
         ////////////////////////////////////////////////////////////////////////////////////////////
         // NativeWindow::Implementation
@@ -37,7 +38,7 @@ namespace AzFramework
         uint32_t GetDisplayRefreshRate() const override;        
 
         ////////////////////////////////////////////////////////////////////////////////////////////
-        // LinuxXcbEventHandlerBus::Handler
+        // XcbEventHandlerBus::Handler
         void HandleXcbEvent(xcb_generic_event_t* event) override;
 
     private:
@@ -49,6 +50,4 @@ namespace AzFramework
         xcb_atom_t          m_xcbAtomProtocols;
         xcb_atom_t          m_xcbAtomDeleteWindow;
     };
-#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-
 } // namespace AzFramework

+ 18 - 0
Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake

@@ -0,0 +1,18 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    AzFramework/XcbApplication.cpp
+    AzFramework/XcbApplication.h
+    AzFramework/XcbConnectionManager.h
+    AzFramework/XcbInputDeviceKeyboard.cpp
+    AzFramework/XcbInputDeviceKeyboard.h
+    AzFramework/XcbInterface.h
+    AzFramework/XcbNativeWindow.cpp
+    AzFramework/XcbNativeWindow.h
+)

+ 0 - 54
Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h

@@ -12,10 +12,6 @@
 #include <AzCore/Interface/Interface.h>
 #include <AzCore/EBus/EBus.h>
 
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-#include <xcb/xcb.h>
-#endif // LY_COMPILE_DEFINITIONS
-
 namespace AzFramework
 {
     class LinuxLifecycleEvents
@@ -30,54 +26,4 @@ namespace AzFramework
 
         using Bus = AZ::EBus<LinuxLifecycleEvents>;
     };
-
-#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-    class LinuxXcbConnectionManager
-    {
-    public:
-        AZ_RTTI(LinuxXcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
-
-        virtual ~LinuxXcbConnectionManager() = default;
-
-        virtual xcb_connection_t* GetXcbConnection() const = 0;
-    };
-
-    class LinuxXcbConnectionManagerBusTraits
-        : public AZ::EBusTraits
-    {
-        public:
-            //////////////////////////////////////////////////////////////////////////
-            // EBusTraits overrides
-            static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
-            static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
-            //////////////////////////////////////////////////////////////////////////
-    };
-
-    using LinuxXcbConnectionManagerBus = AZ::EBus<LinuxXcbConnectionManager, LinuxXcbConnectionManagerBusTraits>;
-    using LinuxXcbConnectionManagerInterface = AZ::Interface<LinuxXcbConnectionManager>;
-    
-    class LinuxXcbEventHandler
-    {
-    public:
-        AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
-
-        virtual ~LinuxXcbEventHandler() = default;
-
-        virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
-    };
-
-    class LinuxXcbEventHandlerBusTraits
-        : public AZ::EBusTraits
-    {
-        public:
-            //////////////////////////////////////////////////////////////////////////
-            // EBusTraits overrides
-            static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
-            static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
-            //////////////////////////////////////////////////////////////////////////
-    };
-
-    using LinuxXcbEventHandlerBus = AZ::EBus<LinuxXcbEventHandler, LinuxXcbEventHandlerBusTraits>;
-
-#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
 } // namespace AzFramework

+ 4 - 2
Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp

@@ -8,7 +8,9 @@
 
 #include <AzFramework/Application/Application.h>
 
-#include "Application_Linux_xcb.h"
+#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+#include <AzFramework/XcbApplication.h>
+#endif
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 namespace AzFramework
@@ -17,7 +19,7 @@ namespace AzFramework
     Application::Implementation* Application::Implementation::Create()
     {
 #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-        return aznew ApplicationLinux_xcb();
+        return aznew XcbApplication();
 #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
         #error "Linux Window Manager Wayland not supported."
         return nullptr;

+ 27 - 0
Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+#include <AzFramework/XcbInputDeviceKeyboard.h>
+#endif
+
+namespace AzFramework
+{
+    InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
+    {
+#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+        return aznew XcbInputDeviceKeyboard(inputDevice);
+#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
+        #error "Linux Window Manager Wayland not supported."
+        return nullptr;
+#else
+        #error "Linux Window Manager not recognized."
+        return nullptr;
+#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+    }
+} // namespace AzFramework

+ 4 - 3
Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp

@@ -6,14 +6,16 @@
  *
  */
 
-#include "NativeWindow_Linux_xcb.h"
+#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+#include <AzFramework/XcbNativeWindow.h>
+#endif
 
 namespace AzFramework
 {
     NativeWindow::Implementation* NativeWindow::Implementation::Create()
     {
 #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
-        return aznew NativeWindowImpl_Linux_xcb();
+        return aznew XcbNativeWindow();
 #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
         #error "Linux Window Manager Wayland not supported."
         return nullptr;
@@ -22,5 +24,4 @@ namespace AzFramework
         return nullptr;
 #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
     }
-
 } // namespace AzFramework

+ 8 - 2
Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake

@@ -10,6 +10,14 @@
 # Only 'xcb' and 'wayland' are recognized
 if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
 
+    set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
+    set(LY_INCLUDE_DIRECTORIES
+        PUBLIC
+            Platform/Common/Xcb
+    )
+    set(LY_FILES_CMAKE
+        Platform/Common/Xcb/azframework_xcb_files.cmake
+    )
     set(LY_BUILD_DEPENDENCIES
         PRIVATE
             3rdParty::X11::xcb
@@ -18,8 +26,6 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
             3rdParty::X11::xkbcommon_X11
     )
 
-    set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
-
 elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
 
     set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND)

+ 1 - 5
Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake

@@ -12,8 +12,6 @@ set(FILES
     AzFramework/API/ApplicationAPI_Platform.h
     AzFramework/API/ApplicationAPI_Linux.h
     AzFramework/Application/Application_Linux.cpp
-    AzFramework/Application/Application_Linux_xcb.h
-    AzFramework/Application/Application_Linux_xcb.cpp
     AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp
     AzFramework/Process/ProcessWatcher_Linux.cpp
     AzFramework/Process/ProcessCommon.h
@@ -22,10 +20,8 @@ set(FILES
     ../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp
     ../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp
     AzFramework/Windowing/NativeWindow_Linux.cpp
-    AzFramework/Windowing/NativeWindow_Linux_xcb.h
-    AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
     ../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
-    AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp
+    AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
     ../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
     ../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
     ../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp

+ 5 - 1
Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp

@@ -10,6 +10,10 @@
 #include <RHI/Instance.h>
 #include <RHI/WSISurface.h>
 
+#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
+#include <AzFramework/XcbConnectionManager.h>
+#endif
+
 namespace AZ
 {
     namespace Vulkan
@@ -21,7 +25,7 @@ namespace AZ
 #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
 
             xcb_connection_t* xcb_connection = nullptr;
-            if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
+            if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
                 xcbConnectionManager != nullptr)
             {
                 xcb_connection = xcbConnectionManager->GetXcbConnection();