diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 9787686d7a..fcc0c1d0df 100755 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -40,6 +40,8 @@ set(VOLK_PULL_IN_VULKAN OFF CACHE INTERNAL "" FORCE) # Needed to make sure vulka if (WIN32) set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR) +elseif(UNIX AND NOT ANDROID AND NOT APPLE) + set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_XCB_KHR) endif() add_subdirectory(volk volk EXCLUDE_FROM_ALL) diff --git a/include/nbl/core/string/StringLiteral.h b/include/nbl/core/string/StringLiteral.h index e79fd044b8..28978aec1a 100644 --- a/include/nbl/core/string/StringLiteral.h +++ b/include/nbl/core/string/StringLiteral.h @@ -17,6 +17,11 @@ struct StringLiteral std::copy_n(str, N, value); } + // the size includes the null terminator + constexpr size_t size() const { + return N; + } + char value[N]; }; diff --git a/include/nbl/ui/CClipboardManagerXCB.h b/include/nbl/ui/CClipboardManagerXCB.h new file mode 100644 index 0000000000..b7fa6f2d6a --- /dev/null +++ b/include/nbl/ui/CClipboardManagerXCB.h @@ -0,0 +1,40 @@ +#ifndef _NBL_UI_C_CLIPBOARD_MANAGER_XCB_INCLUDED_ +#define _NBL_UI_C_CLIPBOARD_MANAGER_XCB_INCLUDED_ + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/core/decl/Types.h" +#include "nbl/ui/IClipboardManagerXCB.h" +namespace nbl::ui +{ + +class IWindowXCB; +class XCBConnection; + +// details on XCB clipboard protocol: https://tronche.com/gui/x/icccm/sec-2.html#s-2 +// class NBL_API2 CClipboardManagerXCB final : public IClipboardManagerXCB +// { +// public: +// inline CClipboardManagerXCB(core::smart_refctd_ptr&& connect): +// IClipboardManagerXCB(), +// m_connection(std::move(connect)) {} + +// virtual std::string getClipboardText() override; +// virtual bool setClipboardText(const std::string_view& data) override; + +// void process(const IWindowXCB* window, xcb_generic_event_t* event) override; +// private: +// core::smart_refctd_ptr m_connection; +// std::mutex m_clipboardMutex; +// std::condition_variable m_clipboardResponseCV; +// std::string m_clipboardResponse; // data sent to the clipboard by another application + +// std::string m_savedClipboard; // data saved to the clipboard for another application to read + +// }; + +} + +#endif + +#endif \ No newline at end of file diff --git a/include/nbl/ui/CCursorControlXCB.h b/include/nbl/ui/CCursorControlXCB.h new file mode 100644 index 0000000000..da6ef1e8cc --- /dev/null +++ b/include/nbl/ui/CCursorControlXCB.h @@ -0,0 +1,32 @@ +#ifndef __NBL_SYSTEM_C_CURSOR_CONTROL_XCB_H_INCLUDED__ +#define __NBL_SYSTEM_C_CURSOR_CONTROL_XCB_H_INCLUDED__ + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/ui/ICursorControl.h" + +namespace nbl::ui +{ + +class XCBConnection; +class NBL_API2 CCursorControlXCB final : public ICursorControl +{ + public: + inline CCursorControlXCB() {} + + void setVisible(bool visible) override; + bool isVisible() const override; + + void setPosition(SPosition pos) override; + void setRelativePosition(IWindow* window, SRelativePosition pos) override; + + SPosition getPosition() override; + SRelativePosition getRelativePosition(IWindow* window) override; + private: + // core::smart_refctd_ptr m_connection; +}; +} + +#endif + +#endif \ No newline at end of file diff --git a/include/nbl/ui/CWindowManagerXCB.h b/include/nbl/ui/CWindowManagerXCB.h new file mode 100644 index 0000000000..32ec08af22 --- /dev/null +++ b/include/nbl/ui/CWindowManagerXCB.h @@ -0,0 +1,46 @@ +#ifndef _NBL_UI_C__WINDOWMANAGER_XCB_INCLUDED_ +#define _NBL_UI_C__WINDOWMANAGER_XCB_INCLUDED_ + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/core/decl/Types.h" + +#include "nbl/ui/IWindow.h" +#include "nbl/ui/IWindowManagerXCB.h" + +namespace nbl::ui +{ + +class CWindowManagerXCB final : public IWindowManagerXCB +{ +public: + + bool setWindowSize_impl(IWindow* window, uint32_t width, uint32_t height) override; + bool setWindowPosition_impl(IWindow* window, int32_t x, int32_t y) override; + bool setWindowRotation_impl(IWindow* window, bool landscape) override; + bool setWindowVisible_impl(IWindow* window, bool visible) override; + bool setWindowMaximized_impl(IWindow* window, bool maximized) override; + + inline SDisplayInfo getPrimaryDisplayInfo() const override final { + return SDisplayInfo(); + } + + CWindowManagerXCB(); + ~CWindowManagerXCB() override = default; + + core::smart_refctd_ptr createWindow(IWindow::SCreationParams&& creationParams) override; + + void destroyWindow(IWindow* wnd) override final {} + + const Xcb& getXcbFunctionTable() const override { return m_xcb; } + const XcbIcccm& getXcbIcccmFunctionTable() const override { return m_xcbIcccm; } + +private: + Xcb m_xcb = Xcb("xcb"); // function tables + XcbIcccm m_xcbIcccm = XcbIcccm("xcb-icccm"); +}; + + +} +#endif +#endif \ No newline at end of file diff --git a/include/nbl/ui/CWindowXCB.h b/include/nbl/ui/CWindowXCB.h new file mode 100644 index 0000000000..47b6389e34 --- /dev/null +++ b/include/nbl/ui/CWindowXCB.h @@ -0,0 +1,71 @@ +#ifndef _NBL_UI_C_WINDOW_XCB_H_INCLUDED_ +#define _NBL_UI_C_WINDOW_XCB_H_INCLUDED_ + +#include "nbl/core/decl/smart_refctd_ptr.h" +#include "nbl/ui/IClipboardManagerXCB.h" +#include "nbl/ui/IWindowXCB.h" +#include "nbl/ui/XCBHandle.h" + +#include + +namespace nbl::ui +{ + +class CWindowManagerXCB; +class XCBConnection; +class CCursorControlXCB; +class IClipboardManagerXCB; + +class NBL_API2 CWindowXCB final : public IWindowXCB +{ + +public: + CWindowXCB(native_handle_t&& handle, core::smart_refctd_ptr&& winManager, SCreationParams&& params); + ~CWindowXCB(); + + const native_handle_t* getNativeHandle() const override { + return &m_handle; + } + + virtual IClipboardManager* getClipboardManager() override; + virtual ICursorControl* getCursorControl() override; + virtual IWindowManager* getManager() const override; + + virtual void setCaption(const std::string_view& caption) override; + +private: + CWindowXCB(core::smart_refctd_ptr&& sys, uint32_t _w, uint32_t _h, E_CREATE_FLAGS _flags); + + native_handle_t m_handle; + core::smart_refctd_ptr m_windowManager; + core::smart_refctd_ptr m_cursorControl; + core::smart_refctd_ptr m_clipboardManager; + + class CDispatchThread final : public system::IThreadHandler + { + public: + using base_t = system::IThreadHandler; + + inline CDispatchThread(CWindowXCB& window) : + base_t(base_t::start_on_construction_t {}), + m_window(window) { + + } + inline ~CDispatchThread() { + } + + inline void init() {} + inline void exit() {} + void work(lock_t& lock); + + inline bool wakeupPredicate() const { return true; } + inline bool continuePredicate() const { return true; } + private: + CWindowXCB& m_window; + friend class CWindowXCB; + } m_dispatcher; +}; + +} + +#endif diff --git a/include/nbl/ui/IClipboardManagerXCB.h b/include/nbl/ui/IClipboardManagerXCB.h new file mode 100644 index 0000000000..8c853545a6 --- /dev/null +++ b/include/nbl/ui/IClipboardManagerXCB.h @@ -0,0 +1,28 @@ +#ifndef _NBL_UI_I_CLIPBOARD_MANAGER_XCB_INCLUDED_ +#define _NBL_UI_I_CLIPBOARD_MANAGER_XCB_INCLUDED_ + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/ui/IClipboardManager.h" + +namespace nbl::ui +{ +class XCBConnection; + +// details on XCB clipboard protocol: https://tronche.com/gui/x/icccm/sec-2.html#s-2 +class NBL_API2 IClipboardManagerXCB : public IClipboardManager +{ + public: + IClipboardManagerXCB() : IClipboardManager() {} + virtual ~IClipboardManagerXCB() = default; + + virtual std::string getClipboardText() = 0; + virtual bool setClipboardText(const std::string_view& data) = 0; + virtual void process(const IWindowXCB* window, xcb_generic_event_t* event) = 0; +}; + +} + +#endif + +#endif \ No newline at end of file diff --git a/include/nbl/ui/IWindowManagerXCB.h b/include/nbl/ui/IWindowManagerXCB.h new file mode 100644 index 0000000000..9d9838c6db --- /dev/null +++ b/include/nbl/ui/IWindowManagerXCB.h @@ -0,0 +1,65 @@ +#ifndef _NBL_UI_I_WINDOWMANAGER_XCB_INCLUDED_ +#define _NBL_UI_I_WINDOWMANAGER_XCB_INCLUDED_ + +#include "nbl/ui/IWindowManager.h" + +#ifdef _NBL_PLATFORM_LINUX_ + +#include +#include +#include + +namespace nbl::ui { + +class IWindowManagerXCB : public IWindowManager +{ + public: + NBL_SYSTEM_DECLARE_DYNAMIC_FUNCTION_CALLER_CLASS(Xcb, system::DefaultFuncPtrLoader, + xcb_destroy_window, + xcb_generate_id, + xcb_create_window, + xcb_connect, + xcb_disconnect, + xcb_map_window, + xcb_get_setup, + xcb_setup_roots_iterator, + xcb_flush, + xcb_intern_atom, + xcb_intern_atom_reply, + xcb_unmap_window, + xcb_get_property, + xcb_get_property_reply, + xcb_get_property_value_length, + xcb_change_property, + xcb_configure_window_checked, + xcb_get_property_value, + xcb_wait_for_event, + xcb_send_event, + xcb_request_check, + xcb_delete_property, + xcb_change_window_attributes, + xcb_warp_pointer, + xcb_query_pointer, + xcb_query_pointer_reply, + xcb_get_selection_owner_reply, + xcb_get_selection_owner + ); + + NBL_SYSTEM_DECLARE_DYNAMIC_FUNCTION_CALLER_CLASS(XcbIcccm, system::DefaultFuncPtrLoader, + xcb_icccm_set_wm_hints, + xcb_icccm_size_hints_set_size, + xcb_icccm_size_hints_set_min_size, + xcb_icccm_size_hints_set_max_size, + xcb_icccm_set_wm_normal_hints + ); + + + NBL_API2 static core::smart_refctd_ptr create(); + virtual const Xcb& getXcbFunctionTable() const = 0; + virtual const XcbIcccm& getXcbIcccmFunctionTable() const = 0; +}; + +} // namespace nbl::ui + +#endif +#endif \ No newline at end of file diff --git a/include/nbl/ui/IWindowXCB.h b/include/nbl/ui/IWindowXCB.h new file mode 100644 index 0000000000..8cad85701b --- /dev/null +++ b/include/nbl/ui/IWindowXCB.h @@ -0,0 +1,36 @@ +#ifndef __NBL_I_WINDOW_XCB_H_INCLUDED__ +#define __NBL_I_WINDOW_XCB_H_INCLUDED__ + +#include "nbl/ui/XCBHandle.h" +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/core/util/bitflag.h" + +#include "nbl/ui/IWindow.h" + +#include + +namespace nbl::ui +{ + +class NBL_API2 IWindowXCB : public IWindow +{ + protected: + virtual ~IWindowXCB() = default; + inline IWindowXCB(SCreationParams&& params) : IWindow(std::move(params)) {} + + public: + using IWindow::IWindow; + + struct native_handle_t { + xcb_window_t m_window; + core::smart_refctd_ptr m_connection; + }; + + virtual const native_handle_t* getNativeHandle() const = 0; +}; + +} + +#endif +#endif \ No newline at end of file diff --git a/include/nbl/ui/XCBHandle.h b/include/nbl/ui/XCBHandle.h new file mode 100644 index 0000000000..a07a116d30 --- /dev/null +++ b/include/nbl/ui/XCBHandle.h @@ -0,0 +1,227 @@ +#ifndef C_XCB_CONNECTION_XCB +#define C_XCB_CONNECTION_XCB + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/core/decl/Types.h" +#include "nbl/core/string/StringLiteral.h" +#include "nbl/core/decl/smart_refctd_ptr.h" +#include "nbl/ui/IWindowManagerXCB.h" + +#include +#include +#include + +#include + +namespace nbl::ui::xcb +{ + class XCBHandle; + + enum MotifFlags: uint32_t { + MWM_HINTS_NONE = 0, + MWM_HINTS_FUNCTIONS = (1L << 0), + MWM_HINTS_DECORATIONS = (1L << 1), + MWM_HINTS_INPUT_MODE = (1L << 2), + MWM_HINTS_STATUS = (1L << 3), + }; + + enum MotifFunctions: uint32_t { + MWM_FUNC_NONE = 0, + MWM_FUNC_ALL = (1L << 0), + MWM_FUNC_RESIZE = (1L << 1), + MWM_FUNC_MOVE = (1L << 2), + MWM_FUNC_MINIMIZE = (1L << 3), + MWM_FUNC_MAXIMIZE = (1L << 4), + MWM_FUNC_CLOSE = (1L << 5), + }; + + enum MotifDecorations: uint32_t { + MWM_DECOR_NONE = 0, + MWM_DECOR_ALL = (1L << 0), + MWM_DECOR_BORDER = (1L << 1), + MWM_DECOR_RESIZEH = (1L << 2), + MWM_DECOR_TITLE = (1L << 3), + MWM_DECOR_MENU = (1L << 4), + MWM_DECOR_MINIMIZE = (1L << 5), + MWM_DECOR_MAXIMIZE = (1L << 6), + }; + + // insane magic in xcb for window hinting good luck finding documentation + // https://fossies.org/linux/motif/lib/Xm/MwmUtil.h + struct MotifWmHints { + MotifFlags flags = MotifFlags::MWM_HINTS_NONE; + MotifFunctions functions = MotifFunctions::MWM_FUNC_NONE; + MotifDecorations decorations = MotifDecorations::MWM_DECOR_NONE; + uint32_t input_mode = 0; // unused + uint32_t status = 0; // unused + }; + + inline MotifWmHints createFlagsToMotifWmHints(IWindow::E_CREATE_FLAGS flags) { + core::bitflag motifFlags(MWM_HINTS_NONE); + core::bitflag motifFunctions(MWM_FUNC_NONE); + core::bitflag motifDecorations(MWM_DECOR_NONE); + motifFlags |= MWM_HINTS_DECORATIONS; + + if (flags & IWindow::ECF_BORDERLESS) { + motifDecorations |= MWM_DECOR_ALL; + } else { + motifDecorations |= MWM_DECOR_BORDER; + motifDecorations |= MWM_DECOR_RESIZEH; + motifDecorations |= MWM_DECOR_TITLE; + + // minimize button + if(flags & IWindow::ECF_MINIMIZED) { + motifDecorations |= MWM_DECOR_MINIMIZE; + motifFunctions |= MWM_FUNC_MINIMIZE; + } + + // maximize button + if(flags & IWindow::ECF_MAXIMIZED) { + motifDecorations |= MWM_DECOR_MAXIMIZE; + motifFunctions |= MWM_FUNC_MAXIMIZE; + } + + // close button + motifFunctions |= MWM_FUNC_CLOSE; + } + + if(motifFunctions.value != MWM_FUNC_NONE) { + motifFlags |= MWM_HINTS_FUNCTIONS; + motifFunctions |= MWM_FUNC_RESIZE; + motifFunctions |= MWM_FUNC_MOVE; + } else { + motifFunctions = MWM_FUNC_ALL; + } + + MotifWmHints hints; + hints.flags = motifFlags.value; + hints.functions = motifFunctions.value; + hints.decorations = motifDecorations.value; + hints.input_mode = 0; + hints.status = 0; + return hints; + } + + class XCBHandle final : public core::IReferenceCounted { + public: + struct XCBHandleToken { + private: + xcb_atom_t m_token = 0; + public: + inline operator xcb_atom_t() { return m_token;} + friend class XCBHandle; + }; + + XCBHandle(core::smart_refctd_ptr&& windowManager): + m_windowManager(std::move(windowManager)) { + const auto& xcb = m_windowManager->getXcbFunctionTable(); + m_connection = xcb.pxcb_connect(nullptr, nullptr); + + struct { + const char* name; + XCBHandleToken* token; + } handles[] = { + {"WM_DELETE_WINDOW", &WM_DELETE_WINDOW}, + {"WM_PROTOCOLS", &WM_PROTOCOLS}, + {"_NET_WM_PING", &_NET_WM_PING}, + {"_NET_WM_STATE_MAXIMIZED_VERT", &_NET_WM_STATE_MAXIMIZED_VERT}, + {"_NET_WM_STATE_MAXIMIZED_HORZ", &_NET_WM_STATE_MAXIMIZED_HORZ}, + {"_NET_WM_STATE_FULLSCREEN", &_NET_WM_STATE_FULLSCREEN}, + {"_NET_WM_STATE", &_NET_WM_STATE}, + {"_MOTIF_WM_HINTS", &_MOTIF_WM_HINTS}, + {"NET_WM_STATE_ABOVE", &NET_WM_STATE_ABOVE}, + }; + std::array cookies; + for(size_t i = 0; i < std::size(handles); ++i) { + cookies[i] = xcb.pxcb_intern_atom(m_connection, false, strlen(handles[i].name), handles[i].name); + } + for (size_t i = 0; i < std::size(handles); ++i) { + xcb_intern_atom_reply_t *reply = xcb.pxcb_intern_atom_reply(m_connection, cookies[i], nullptr); + handles[i].token->m_token = reply->atom; + free(reply); + } + } + ~XCBHandle() { + if(m_connection) { + const auto& xcb = m_windowManager->getXcbFunctionTable(); + xcb.pxcb_disconnect(m_connection); + } + } + + inline IWindowManagerXCB* windowManager() const { return m_windowManager.get(); } + inline const IWindowManagerXCB::Xcb& getXcbFunctionTable() const { return m_windowManager->getXcbFunctionTable(); } + inline const IWindowManagerXCB::XcbIcccm& getXcbIcccmFunctionTable() const { return m_windowManager->getXcbIcccmFunctionTable(); } + inline operator xcb_connection_t*() { return m_connection; } + inline xcb_connection_t* getNativeHandle() { return m_connection; } + + XCBHandleToken WM_DELETE_WINDOW; + XCBHandleToken WM_PROTOCOLS; + XCBHandleToken _NET_WM_PING; + + XCBHandleToken _NET_WM_STATE_MAXIMIZED_VERT; + XCBHandleToken _NET_WM_STATE_MAXIMIZED_HORZ; + XCBHandleToken _NET_WM_STATE_FULLSCREEN; + XCBHandleToken _NET_WM_STATE; + XCBHandleToken _MOTIF_WM_HINTS; + XCBHandleToken NET_WM_STATE_ABOVE; + private: + core::smart_refctd_ptr m_windowManager; + xcb_connection_t* m_connection = nullptr; + }; + + inline void setMotifWmHints(XCBHandle& handle, xcb_window_t window, const MotifWmHints& hint) { + auto& xcb = handle.getXcbFunctionTable(); + + if(hint.flags != MotifFlags::MWM_HINTS_NONE) { + xcb.pxcb_change_property(handle.getNativeHandle(), XCB_PROP_MODE_REPLACE, window, + handle._MOTIF_WM_HINTS, + handle._MOTIF_WM_HINTS, 32, sizeof(MotifWmHints) / sizeof(uint32_t), &hint); + } else { + xcb.pxcb_delete_property(handle.getNativeHandle(), window, handle._MOTIF_WM_HINTS); + } + } + + inline void setNetMWState(XCBHandle& handle, xcb_window_t rootWindow, + xcb_window_t window, + bool set, + xcb_atom_t first, + xcb_atom_t second = XCB_NONE) { + auto& xcb = handle.getXcbFunctionTable(); + + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.type = handle._NET_WM_STATE; + event.window = window; + event.format = 32; + event.sequence = 0; + event.data.data32[0] = set ? 1l : 0l; + event.data.data32[1] = first; + event.data.data32[2] = second; + event.data.data32[3] = 1; + event.data.data32[4] = 0; + xcb.pxcb_send_event(handle, 0, rootWindow, + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, reinterpret_cast(&event)); + } + + inline const xcb_screen_t* primaryScreen(XCBHandle& handle) { + auto& xcb = handle.getXcbFunctionTable(); + const xcb_setup_t *setup = xcb.pxcb_get_setup(handle); + xcb_screen_t *screen = xcb.pxcb_setup_roots_iterator(setup).data; + return screen; + } + + inline bool checkCookie(XCBHandle& handle, xcb_void_cookie_t cookie) { + auto& xcb = handle.getXcbFunctionTable(); + if (xcb_generic_error_t* error = xcb.pxcb_request_check(handle, cookie)) + { + printf("XCB error: %d", error->error_code); + return false; + } + return true; + } + +} + +#endif +#endif // C_XCB_HANDLER_XCB diff --git a/include/nbl/ui/declarations.h b/include/nbl/ui/declarations.h index 4a2bce9552..72947d82f4 100644 --- a/include/nbl/ui/declarations.h +++ b/include/nbl/ui/declarations.h @@ -13,6 +13,7 @@ #elif defined(_NBL_BUILD_WITH_WAYLAND) && defined(_NBL_TEST_WAYLAND) # include "nbl/ui/CWindowManagerWayland.h" #elif defined(_NBL_PLATFORM_LINUX_) +# include "nbl/ui/CWindowManagerXCB.h" #endif // TODO more platforms (android) // clipboards @@ -22,4 +23,4 @@ #include "nbl/ui/IInputEventChannel.h" -#endif \ No newline at end of file +#endif diff --git a/include/nbl/ui/definitions.h b/include/nbl/ui/definitions.h index 1eab60d597..9d1d4afbc9 100644 --- a/include/nbl/ui/definitions.h +++ b/include/nbl/ui/definitions.h @@ -1,10 +1,18 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_UI_DEFINITIONS_H_INCLUDED_ -#define _NBL_UI_DEFINITIONS_H_INCLUDED_ + +#ifndef __NBL_UI_DEFINITIONS_H_INCLUDED__ +#define __NBL_UI_DEFINITIONS_H_INCLUDED__ // dependencies #include "nbl/system/definitions.h" +// windows +#include "nbl/ui/CWindowManagerWin32.h" +#include "nbl/ui/CWindowManagerXCB.h" + +#include "nbl/ui/CWindowWin32.h" +#include "nbl/ui/CWindowXCB.h" + #endif \ No newline at end of file diff --git a/include/nbl/video/CVulkanConnection.h b/include/nbl/video/CVulkanConnection.h index 71b8e02e8c..0d03a4b0ee 100644 --- a/include/nbl/video/CVulkanConnection.h +++ b/include/nbl/video/CVulkanConnection.h @@ -6,6 +6,10 @@ #if defined(_NBL_PLATFORM_WINDOWS_) # include "nbl/ui/IWindowWin32.h" +#elif defined(_NBL_PLATFORM_LINUX_) +# include "nbl/ui/IWindowXCB.h" +#else +# error "Unsupported platform" #endif #include diff --git a/include/nbl/video/surface/CSurfaceVulkan.h b/include/nbl/video/surface/CSurfaceVulkan.h index 203f580f67..0ff4dc29fe 100644 --- a/include/nbl/video/surface/CSurfaceVulkan.h +++ b/include/nbl/video/surface/CSurfaceVulkan.h @@ -3,6 +3,7 @@ #include "BuildConfigOptions.h" +#include "nbl/ui/IWindowXCB.h" #include "nbl/video/surface/ISurface.h" #include "nbl/video/CVulkanConnection.h" @@ -45,9 +46,21 @@ class NBL_API2 CSurfaceVulkanWin32 final : public CSurface create(core::smart_refctd_ptr&& api, core::smart_refctd_ptr&& window); }; -#elif defined _NBL_PLATFORM_LINUX_ -// TODO: later, not this week -#elif defined _NBL_PLATFORM_ANDROID_ +#elif defined(_NBL_PLATFORM_LINUX_) +class NBL_API2 CSurfaceVulkanXcb final : public CSurface +{ + using this_t = CSurfaceVulkanXcb; + using base_t = CSurface; +public: + inline CSurfaceVulkanXcb(core::smart_refctd_ptr&& window, core::smart_refctd_ptr&& api, VkSurfaceKHR surf) : + base_t(std::move(window), std::move(api), surf) + { + } + + static core::smart_refctd_ptr create(core::smart_refctd_ptr&& api, core::smart_refctd_ptr&& window); +}; + +#elif defined(_NBL_PLATFORM_ANDROID_) // TODO: later, not this week #endif } diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 878d658ae3..00576d84c9 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -184,7 +184,11 @@ set(NBL_SYSTEM_SOURCES ) set(NBL_UI_SOURCES ${NBL_ROOT_PATH}/src/nbl/ui/CWindowWin32.cpp - ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerWin32.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CWindowXCB.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerWin32.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerXCB.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CClipboardManagerXCB.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CCursorControlXCB.cpp ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerAndroid.cpp ${NBL_ROOT_PATH}/src/nbl/ui/CGraphicalApplicationAndroid.cpp ) @@ -556,9 +560,8 @@ endif() # Linux Display Systems if (UNIX AND NOT ANDROID AND NOT APPLE) target_include_directories(Nabla PUBLIC - ${X11_INCLUDE_DIR} - X11_Xrandr_INCLUDE_PATH - ${X11_xf86vmode_INCLUDE_PATH} + ${X11_xcb_INCLUDE_PATH} + ${X11_xcb_icccm_INCLUDE_PATH} ) endif() @@ -569,6 +572,10 @@ target_link_libraries(Nabla PRIVATE volk) if(WIN32) target_compile_definitions(Nabla PRIVATE VK_USE_PLATFORM_WIN32_KHR) endif() +if(UNIX AND NOT ANDROID AND NOT APPLE) + target_compile_definitions(Nabla PRIVATE VK_USE_PLATFORM_XCB_KHR) +endif() + # CUDA if (NBL_COMPILE_WITH_CUDA) target_include_directories(Nabla PUBLIC ${CUDAToolkit_INCLUDE_DIRS}) diff --git a/src/nbl/ui/CClipboardManagerXCB.cpp b/src/nbl/ui/CClipboardManagerXCB.cpp new file mode 100644 index 0000000000..ae40c2ced8 --- /dev/null +++ b/src/nbl/ui/CClipboardManagerXCB.cpp @@ -0,0 +1,146 @@ + +#include "nbl/ui/CClipboardManagerXCB.h" + +#include +#include +#include + +#include + +namespace nbl::ui +{ + // std::string CClipboardManagerXCB::getClipboardText() { + // { + // std::unique_lock lk(m_clipboardMutex); + // m_clipboardResponseCV.wait_until(lk, std::chrono::system_clock::now() + std::chrono::seconds(1)); + // } + // std::lock_guard lk(m_clipboardMutex); + // std::string response = std::move(m_clipboardResponse); + // m_clipboardResponse = std::string(); + // return response; + // } + + + // bool CClipboardManagerXCB::setClipboardText(const std::string_view& data) { + // std::lock_guard lk(m_clipboardMutex); + // m_savedClipboard = data; + // return true; + // } + + // void CClipboardManagerXCB::process(const IWindowXCB* window, xcb_generic_event_t* event) { + // const auto& xcb = m_connection->getXcbFunctionTable(); + // auto* windowHandle = window->getNativeHandle(); + // auto TARGETS = m_connection->resolveAtom(m_TARGETS); + + // switch(event->response_type & ~0x80) { + // // XCB_ATOM + // // Somone is requesting the clipboard data + // case XCB_SELECTION_REQUEST: { + // auto* sne = reinterpret_cast(event); + // if(sne->requestor == windowHandle->m_window) { + // if(sne->target == TARGETS) { + // std::vector targets; + // { + // std::lock_guard lk(m_clipboardMutex); + // for(auto& format : { + // m_connection->resolveAtom(m_formatUTF8_0), + // m_connection->resolveAtom(m_formatUTF8_1), + // m_connection->resolveAtom(m_formatUTF8_2), + // m_connection->resolveAtom(m_formatGTK), + // m_connection->resolveAtom(m_formatString), + // m_connection->resolveAtom(m_formatText), + // m_connection->resolveAtom(m_formatTextPlain) + // }) { + // targets.push_back(format); + // } + // } + // targets.push_back(m_connection->resolveAtom(m_TARGETS)); + // xcb.pxcb_change_property( + // m_connection->getNativeHandle(), + // XCB_PROP_MODE_REPLACE, + // sne->requestor, + // sne->property, + // XCB_ATOM, + // 8*sizeof(xcb_atom_t), + // targets.size(), + // &targets[0]); + // } else { + // std::lock_guard lk(m_clipboardMutex); + // xcb.pxcb_change_property( + // m_connection->getNativeHandle(), + // XCB_PROP_MODE_REPLACE, + // sne->requestor, + // sne->property, + // sne->target, + // 8, + // m_savedClipboard.size(), + // m_savedClipboard.data()); + // } + // } + + // // Notify the "requestor" that we've already updated the property. + // xcb_selection_notify_event_t notify; + // notify.response_type = XCB_SELECTION_NOTIFY; + // notify.pad0 = 0; + // notify.sequence = 0; + // notify.time = sne->time; + // notify.requestor = sne->requestor; + // notify.selection = sne->selection; + // notify.target = sne->target; + // notify.property = sne->property; + + // xcb.pxcb_send_event(m_connection->getNativeHandle(), false, + // sne->requestor, + // XCB_EVENT_MASK_NO_EVENT, // SelectionNotify events go without mask + // (const char*)¬ify); + + // xcb.pxcb_flush(m_connection->getNativeHandle()); + // break; + // } + // // Someone else has new content in the clipboard, so is + // // notifying us that we should delete our data now. + // case XCB_SELECTION_CLEAR: { + // auto* sne = reinterpret_cast(event); + // if (sne->selection == m_connection->resolveAtom(m_CLIPBOARD)) { + // std::lock_guard lock(m_clipboardMutex); + // m_savedClipboard = std::string(); + // } + // break; + // } + // // we've requested the clipboard data, and this is the reply + // case XCB_SELECTION_NOTIFY: { + // auto* sne = reinterpret_cast(event); + // if(sne->requestor == windowHandle->m_window){ + // // xcb.pxcb_get_a + // xcb_atom_t fieldType = XCB_ATOM; + // if(sne->target != TARGETS) { + // fieldType = sne->target; + // } + // xcb_get_property_cookie_t cookie = xcb.pxcb_get_property(m_connection->getNativeHandle(), true, + // sne->requestor, + // sne->property, + // fieldType, 0, 0x1fffffff); // 0x1fffffff = INT32_MAX / 4 + // if(xcb_get_property_reply_t* reply = + // xcb.pxcb_get_property_reply(m_connection->getNativeHandle(), cookie, nullptr)) { + // core::SRAIIBasedExiter exitReply([reply]() -> void { + // free(reply); + // }); + + // if(reply->type == m_connection->resolveAtom(m_INCR)) { + // assert(false); // TODO + // } else { + // const auto* src = reinterpret_cast(xcb.pxcb_get_property_value(reply)); + // size_t n = xcb.pxcb_get_property_value_length(reply); + // { + // std::lock_guard lk(m_clipboardMutex); + // m_clipboardResponse = std::string(src, n); + // } + // m_clipboardResponseCV.notify_one(); + // } + // } + // } + // break; + // } + // } + // } +} \ No newline at end of file diff --git a/src/nbl/ui/CCursorControlXCB.cpp b/src/nbl/ui/CCursorControlXCB.cpp new file mode 100644 index 0000000000..9a6c1b28ef --- /dev/null +++ b/src/nbl/ui/CCursorControlXCB.cpp @@ -0,0 +1,62 @@ + +#include "nbl/ui/CCursorControlXCB.h" + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/ui/IWindowXCB.h" + +namespace nbl::ui +{ + void CCursorControlXCB::setVisible(bool visible) { + // TODO: implement + } + + bool CCursorControlXCB::isVisible() const { + return true; + } + + void CCursorControlXCB::setPosition(SPosition pos) { + // auto& xcb = m_connection->getXcbFunctionTable(); + // const auto* primaryScreen = m_connection->primaryScreen(); + // xcb.pxcb_warp_pointer(m_connection->getNativeHandle(), XCB_NONE, primaryScreen->root, 0, 0, 0, 0, pos.x, pos.y); + // xcb.pxcb_flush(m_connection->getNativeHandle()); + } + + void CCursorControlXCB::setRelativePosition(IWindow* window, SRelativePosition position) { + // auto* windowXcb = static_cast(window); + // auto& xcb = m_connection->getXcbFunctionTable(); + // auto* handle = windowXcb->getNativeHandle(); + // assert(handle && handle->m_window != XCB_WINDOW_NONE); + + // xcb.pxcb_warp_pointer(m_connection->getNativeHandle(), XCB_NONE, handle->m_window, 0, 0, 0, 0, position.x, position.y); + // xcb.pxcb_flush(m_connection->getNativeHandle()); + } + + CCursorControlXCB::SPosition CCursorControlXCB::getPosition() { + // auto& xcb = m_connection->getXcbFunctionTable(); + // xcb_query_pointer_cookie_t token = xcb.pxcb_query_pointer(m_connection->getNativeHandle(), m_connection->primaryScreen()->root); + // if(auto reply = xcb.pxcb_query_pointer_reply(m_connection->getNativeHandle(), token, nullptr)) { + // core::SRAIIBasedExiter exitReply([reply]() -> void { + // free(reply); + // }); + // return {reply->root_x, reply->root_y}; + // } + return {0, 0}; + } + + CCursorControlXCB::SRelativePosition CCursorControlXCB::getRelativePosition(IWindow* window) { + auto* xcbWidnow = static_cast(window); + auto* windowHandle = xcbWidnow->getNativeHandle(); + + // auto& xcb = m_connection->getXcbFunctionTable(); + // xcb_query_pointer_cookie_t token = xcb.pxcb_query_pointer(m_connection->getNativeHandle(), windowHandle->m_window); + // if(auto reply = xcb.pxcb_query_pointer_reply(m_connection->getNativeHandle(), token, nullptr)) { + // core::SRAIIBasedExiter exitReply([reply]() -> void { + // free(reply); + // }); + // return {static_cast(reply->win_x), static_cast(reply->win_y)}; + // } + return {0, 0}; + } +} +#endif diff --git a/src/nbl/ui/CWindowManagerXCB.cpp b/src/nbl/ui/CWindowManagerXCB.cpp new file mode 100644 index 0000000000..c2ffbb22bb --- /dev/null +++ b/src/nbl/ui/CWindowManagerXCB.cpp @@ -0,0 +1,142 @@ +#include "nbl/ui/CWindowManagerXCB.h" + +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/ui/CWindowManagerXCB.h" +#include "nbl/ui/CWindowXCB.h" + +using namespace nbl; +using namespace nbl::ui; + +core::smart_refctd_ptr IWindowManagerXCB::create() +{ + return core::make_smart_refctd_ptr(); +} + +CWindowManagerXCB::CWindowManagerXCB() { +} + + +core::smart_refctd_ptr CWindowManagerXCB::createWindow(IWindow::SCreationParams&& creationParams) +{ + IWindowXCB::native_handle_t windowHandle = { + 0, + core::make_smart_refctd_ptr(core::smart_refctd_ptr(this)) + }; + const auto* primaryScreen = xcb::primaryScreen(*windowHandle.m_connection); + windowHandle.m_window = m_xcb.pxcb_generate_id(*windowHandle.m_connection); + + uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + uint32_t valueList[] = { + primaryScreen->black_pixel, + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE + }; + + xcb_void_cookie_t result = m_xcb.pxcb_create_window( + *windowHandle.m_connection, XCB_COPY_FROM_PARENT, windowHandle.m_window, primaryScreen->root, + static_cast(creationParams.x), + static_cast(creationParams.y), + static_cast(creationParams.width), + static_cast(creationParams.height), 4, + XCB_WINDOW_CLASS_INPUT_OUTPUT, primaryScreen->root_visual, eventMask, + valueList); + if(m_xcb.pxcb_request_check(*windowHandle.m_connection, result)) { + m_xcb.pxcb_destroy_window(*windowHandle.m_connection, windowHandle.m_window); + m_xcb.pxcb_flush(*windowHandle.m_connection); + return nullptr; + } + + const std::array atoms {windowHandle.m_connection->WM_DELETE_WINDOW, windowHandle.m_connection->_NET_WM_PING}; + m_xcb.pxcb_change_property( + *windowHandle.m_connection, + XCB_PROP_MODE_REPLACE, + windowHandle.m_window, + windowHandle.m_connection->WM_PROTOCOLS, XCB_ATOM_ATOM, 32, atoms.size(), atoms.data()); + + auto motifHints = xcb::createFlagsToMotifWmHints(creationParams.flags); + xcb::setMotifWmHints(*windowHandle.m_connection, windowHandle.m_window, motifHints); + + if(creationParams.flags & IWindow::E_CREATE_FLAGS::ECF_ALWAYS_ON_TOP) { + xcb::setNetMWState(*windowHandle.m_connection, + primaryScreen->root, + windowHandle.m_window, + windowHandle.m_window, + windowHandle.m_connection->NET_WM_STATE_ABOVE); + } + + std::string title = std::string(creationParams.windowCaption); + auto window = core::make_smart_refctd_ptr(std::move(windowHandle), core::smart_refctd_ptr(this), std::move(creationParams)); + window->setCaption(title); + return window; +} + +bool CWindowManagerXCB::setWindowSize_impl(IWindow* window, uint32_t width, uint32_t height) { + auto wnd = static_cast(window); + auto* nativeHandle = wnd->getNativeHandle(); + + xcb_size_hints_t hints = {0}; + m_xcbIcccm.pxcb_icccm_size_hints_set_size(&hints, true, width, height); + m_xcbIcccm.pxcb_icccm_size_hints_set_min_size(&hints, width, height); + m_xcbIcccm.pxcb_icccm_size_hints_set_max_size(&hints, width, height); + m_xcbIcccm.pxcb_icccm_set_wm_normal_hints(*nativeHandle->m_connection, nativeHandle->m_window, &hints); + return true; +} + +bool CWindowManagerXCB::setWindowPosition_impl(IWindow* window, int32_t x, int32_t y) { + auto wnd = static_cast(window); + auto* nativeHandle = wnd->getNativeHandle(); + + const int32_t values[] = { x, y }; + auto cookie = m_xcb.pxcb_configure_window_checked(*nativeHandle->m_connection, nativeHandle->m_window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); + bool check = xcb::checkCookie(*nativeHandle->m_connection, cookie); + m_xcb.pxcb_flush(*nativeHandle->m_connection); + + return check; +} + +bool CWindowManagerXCB::setWindowRotation_impl(IWindow* window, bool landscape) { + auto wnd = static_cast(window); + auto* nativeHandle = wnd->getNativeHandle(); + + + return true; +} + +bool CWindowManagerXCB::setWindowVisible_impl(IWindow* window, bool visible) { + auto wnd = static_cast(window); + auto* handle = wnd->getNativeHandle(); + + if(visible) { + m_xcb.pxcb_map_window(*handle->m_connection, handle->m_window); + m_xcb.pxcb_flush(*handle->m_connection); + } else { + m_xcb.pxcb_unmap_window(*handle->m_connection, handle->m_window); + m_xcb.pxcb_flush(*handle->m_connection); + } + + return true; +} + +bool CWindowManagerXCB::setWindowMaximized_impl(IWindow* window, bool maximized) { + auto wnd = static_cast(window); + auto* handle = wnd->getNativeHandle(); + const auto* primaryScreen = xcb::primaryScreen(*handle->m_connection); + + xcb::setNetMWState( + *handle->m_connection, + primaryScreen->root, + handle->m_window, maximized && !wnd->isBorderless(), handle->m_connection->_NET_WM_STATE_FULLSCREEN); + + xcb::setNetMWState( + *handle->m_connection, + primaryScreen->root, + handle->m_window, maximized && wnd->isBorderless(), + handle->m_connection->_NET_WM_STATE_MAXIMIZED_VERT, + handle->m_connection->_NET_WM_STATE_MAXIMIZED_HORZ); + + m_xcb.pxcb_flush(*handle->m_connection); + return true; +} + +#endif \ No newline at end of file diff --git a/src/nbl/ui/CWindowXCB.cpp b/src/nbl/ui/CWindowXCB.cpp new file mode 100644 index 0000000000..cd187d28c8 --- /dev/null +++ b/src/nbl/ui/CWindowXCB.cpp @@ -0,0 +1,131 @@ +#ifdef _NBL_PLATFORM_LINUX_ + +#include "nbl/core/decl/smart_refctd_ptr.h" +#include "nbl/core/string/StringLiteral.h" + +#include "nbl/system/DefaultFuncPtrLoader.h" + +#include "nbl/ui/IWindowXCB.h" +#include "nbl/ui/CWindowXCB.h" +#include "nbl/ui/CCursorControlXCB.h" +#include "nbl/ui/CWindowManagerXCB.h" + +#include +#include +#include +#include +#include + +namespace nbl::ui { + +static bool checkXcbCookie(const IWindowManagerXCB::Xcb& functionTable, xcb_connection_t* connection, xcb_void_cookie_t cookie) { + if (xcb_generic_error_t* error = functionTable.pxcb_request_check(connection, cookie)) + { + printf("XCB error: %d", error->error_code); + return false; + } + return true; +} + +void CWindowXCB::CDispatchThread::work(lock_t& lock){ + if(m_quit) { + return; + } + auto& xcb = m_window.m_windowManager->getXcbFunctionTable(); + // auto& connection = m_window.m_handle; + auto& windowHandle = m_window.m_handle; + + + if(auto event = xcb.pxcb_wait_for_event(*windowHandle.m_connection)) { + auto* eventCallback = m_window.getEventCallback(); + // m_window.m_clipboardManager->process(&m_window, event); + switch (event->response_type & ~0x80) + { + case 0: { + xcb_generic_error_t* error = reinterpret_cast(event); + printf("XCB error: %d", error->error_code); + break; + } + case XCB_CONFIGURE_NOTIFY: { + xcb_configure_notify_event_t* cne = reinterpret_cast(event); + if(m_window.m_width != cne->width || + m_window.m_height != cne->height) { + eventCallback->onWindowResized(&m_window, cne->width, cne->height); + } + if(m_window.m_x != cne->x || + m_window.m_y != cne->y) { + eventCallback->onWindowMoved(&m_window, cne->x, cne->y); + } + break; + } + case XCB_DESTROY_WINDOW: { + xcb_destroy_window_request_t* dwr = reinterpret_cast(event); + if(dwr->window == windowHandle.m_window) { + m_quit = true; + eventCallback->onWindowClosed(&m_window); + } + break; + } + case XCB_CLIENT_MESSAGE: { + xcb_client_message_event_t* cme = reinterpret_cast(event); + if(cme->data.data32[0] == windowHandle.m_connection->WM_DELETE_WINDOW) { + xcb.pxcb_unmap_window(*windowHandle.m_connection, windowHandle.m_window); + xcb.pxcb_destroy_window(*windowHandle.m_connection, windowHandle.m_window); + xcb.pxcb_flush(*windowHandle.m_connection); + windowHandle.m_window = 0; + m_quit = true; // we need to quit the dispatch thread + eventCallback->onWindowClosed(&m_window); + } else if(cme->data.data32[0] == windowHandle.m_connection->_NET_WM_PING && cme->window != xcb::primaryScreen(*windowHandle.m_connection)->root) { + xcb_client_message_event_t ev = *cme; + ev.response_type = XCB_CLIENT_MESSAGE; + ev.window = m_window.m_handle.m_window; + ev.type = windowHandle.m_connection->_NET_WM_PING; + xcb.pxcb_send_event(*windowHandle.m_connection, 0, m_window.m_handle.m_window, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast(&ev)); + xcb.pxcb_flush(*windowHandle.m_connection); + } + break; + } + } + free(event); + } +} + + +CWindowXCB::CWindowXCB(native_handle_t&& handle, core::smart_refctd_ptr&& winManager, SCreationParams&& params): + IWindowXCB(std::move(params)), + m_handle(std::move(handle)), + m_windowManager(std::move(winManager)), + m_dispatcher(*this) { + + auto& xcb = m_handle.m_connection->getXcbFunctionTable(); + auto& xcbIccm = m_handle.m_connection->getXcbIcccmFunctionTable(); + + xcb.pxcb_map_window(*m_handle.m_connection, m_handle.m_window); + xcb.pxcb_flush(*m_handle.m_connection); + +} + +CWindowXCB::~CWindowXCB() +{ +} + +IClipboardManager* CWindowXCB::getClipboardManager() { + return m_clipboardManager.get(); +} + +ICursorControl* CWindowXCB::getCursorControl() { + return m_cursorControl.get(); +} + +IWindowManager* CWindowXCB::getManager() const { + return m_windowManager.get(); +} + +void CWindowXCB::setCaption(const std::string_view& caption) { + auto& xcb = m_handle.m_connection->getXcbFunctionTable(); + xcb.pxcb_change_property(*m_handle.m_connection, XCB_PROP_MODE_REPLACE, m_handle.m_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast(caption.size()), reinterpret_cast(caption.data())); + xcb.pxcb_flush(*m_handle.m_connection); +} +} + +#endif diff --git a/src/nbl/video/CSurfaceVulkan.cpp b/src/nbl/video/CSurfaceVulkan.cpp index 21fa60681b..22bf1b5353 100644 --- a/src/nbl/video/CSurfaceVulkan.cpp +++ b/src/nbl/video/CSurfaceVulkan.cpp @@ -143,5 +143,31 @@ namespace nbl::video return nullptr; } } +#elif defined(_NBL_PLATFORM_LINUX_) + + #include + core::smart_refctd_ptr CSurfaceVulkanXcb::create(core::smart_refctd_ptr&& api, core::smart_refctd_ptr&& window) + { + if(!api || !window) + return nullptr; + + auto* handle = window->getNativeHandle(); + // assert(handle && handle->m_window != XCB_WINDOW_NONE && handle->m_connection != nullptr); + VkXcbSurfaceCreateInfoKHR createInfo { VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR}; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.connection = *handle->m_connection; + createInfo.window = handle->m_window; + VkSurfaceKHR vk_surface; + if (vkCreateXcbSurfaceKHR(api->getInternalObject(), &createInfo, nullptr, &vk_surface) == VK_SUCCESS) + { + auto retval = new this_t(std::move(window), std::move(api), vk_surface); + return core::smart_refctd_ptr(retval, core::dont_grab); + } + return nullptr; + } + + #endif + } \ No newline at end of file diff --git a/src/nbl/video/CVulkanConnection.cpp b/src/nbl/video/CVulkanConnection.cpp index a648fb613f..34c9497d90 100644 --- a/src/nbl/video/CVulkanConnection.cpp +++ b/src/nbl/video/CVulkanConnection.cpp @@ -151,6 +151,8 @@ namespace nbl::video insertToFeatureSetIfAvailable(VK_KHR_SURFACE_EXTENSION_NAME, "E_SWAPCHAIN_MODE::ESM_SURFACE flag for featureName"); #if defined(_NBL_PLATFORM_WINDOWS_) insertToFeatureSetIfAvailable(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, "E_SWAPCHAIN_MODE::ESM_SURFACE flag for featureName"); +#elif defined(_NBL_PLATFORM_LINUX_) + insertToFeatureSetIfAvailable(VK_KHR_XCB_SURFACE_EXTENSION_NAME, "E_SWAPCHAIN_MODE::ESM_SURFACE flag for featureName"); #endif } SFeatures enabledFeatures = featuresToEnable;