diff --git a/src/Nazara/Renderer/GLX/Display.cpp b/src/Nazara/Renderer/GLX/Display.cpp new file mode 100644 index 000000000..e8c3b51cd --- /dev/null +++ b/src/Nazara/Renderer/GLX/Display.cpp @@ -0,0 +1,246 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + // The shared display and its reference counter + xcb_connection_t* sharedConnection = nullptr; + int screen_nbr = 0; + unsigned int referenceCountConnection = 0; + + xcb_key_symbols_t* sharedkeySymbol = nullptr; + unsigned int referenceCountKeySymbol = 0; + + xcb_ewmh_connection_t* sharedEwmhConnection = nullptr; + unsigned int referenceCountEwmhConnection = 0; + + using AtomMap = std::unordered_map; + AtomMap atoms; + } + + bool X11::CheckCookie(xcb_connection_t* connection, xcb_void_cookie_t cookie) + { + ScopedXCB error(xcb_request_check( + connection, + cookie + )); + + if (error) + return false; + else + return true; + } + + void X11::CloseConnection(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + --referenceCountConnection; + } + + void X11::CloseEWMHConnection(xcb_ewmh_connection_t* ewmh_connection) + { + NazaraAssert(ewmh_connection == sharedEwmhConnection, "The model is meant for one connection to X11 server"); + --referenceCountEwmhConnection; + } + + xcb_atom_t X11::GetAtom(const std::string& name, bool onlyIfExists) + { + auto iter = atoms.find(name); + + if (iter != atoms.end()) + return iter->second; + + ScopedXCB error(nullptr); + + xcb_connection_t* connection = OpenConnection(); + + ScopedXCB reply(xcb_intern_atom_reply( + connection, + xcb_intern_atom( + connection, + onlyIfExists, + name.GetSize(), + name.GetConstBuffer() + ), + &error + )); + + CloseConnection(connection); + + if (error || !reply) + { + NazaraError("Failed to get " + name + " atom."); + return XCB_ATOM_NONE; + } + + atoms[name] = reply->atom; + + return reply->atom; + } + + bool X11::Initialize() + { + if (IsInitialized()) + { + s_moduleReferenceCounter++; + return true; // Déjà initialisé + } + + s_moduleReferenceCounter++; + + NazaraAssert(referenceCountConnection == 0, "Initialize should be called before anything"); + NazaraAssert(referenceCountKeySymbol == 0, "Initialize should be called before anything"); + NazaraAssert(referenceCountEwmhConnection == 0, "Initialize should be called before anything"); + + { + sharedConnection = xcb_connect(nullptr, &screen_nbr); + + // Opening display failed: The best we can do at the moment is to output a meaningful error message + if (!sharedConnection || xcb_connection_has_error(sharedConnection)) + { + NazaraError("Failed to open xcb connection"); + return false; + } + + OpenConnection(); + } + + { + sharedkeySymbol = xcb_key_symbols_alloc(sharedConnection); + + XCBKeySymbolsAlloc(sharedConnection); + } + + { + sharedEwmhConnection = new xcb_ewmh_connection_t; + xcb_intern_atom_cookie_t* ewmh_cookie = xcb_ewmh_init_atoms(sharedConnection, sharedEwmhConnection); + + if(!xcb_ewmh_init_atoms_replies(sharedEwmhConnection, ewmh_cookie, nullptr)) + { + NazaraError("Could not initialize EWMH Connection"); + sharedEwmhConnection = nullptr; + } + + OpenEWMHConnection(sharedConnection); + } + + return true; + } + + bool X11::IsInitialized() + { + return s_moduleReferenceCounter != 0; + } + + xcb_key_symbols_t* X11::XCBKeySymbolsAlloc(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + + ++referenceCountKeySymbol; + return sharedkeySymbol; + } + + void X11::XCBKeySymbolsFree(xcb_key_symbols_t* keySymbols) + { + NazaraAssert(keySymbols == sharedkeySymbol, "The model is meant for one connection to X11 server"); + + --referenceCountKeySymbol; + } + + xcb_connection_t* X11::OpenConnection() + { + ++referenceCountConnection; + return sharedConnection; + } + + xcb_ewmh_connection_t* X11::OpenEWMHConnection(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + + ++referenceCountEwmhConnection; + return sharedEwmhConnection; + } + + void X11::Uninitialize() + { + if (s_moduleReferenceCounter != 1) + { + // Le module est soit encore utilisé, soit pas initialisé + if (s_moduleReferenceCounter > 1) + s_moduleReferenceCounter--; + + return; + } + + s_moduleReferenceCounter = 0; + + { + NazaraAssert(referenceCountEwmhConnection == 1, "Uninitialize should be called after anything or a close is missing"); + CloseEWMHConnection(sharedEwmhConnection); + + xcb_ewmh_connection_wipe(sharedEwmhConnection); + delete sharedEwmhConnection; + } + + { + NazaraAssert(referenceCountKeySymbol == 1, "Uninitialize should be called after anything or a free is missing"); + XCBKeySymbolsFree(sharedkeySymbol); + + xcb_key_symbols_free(sharedkeySymbol); + } + + { + NazaraAssert(referenceCountConnection == 1, "Uninitialize should be called after anything or a close is missing"); + CloseConnection(sharedConnection); + + xcb_disconnect(sharedConnection); + } + } + + xcb_window_t X11::XCBDefaultRootWindow(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + xcb_screen_t* screen = XCBDefaultScreen(connection); + if (screen) + return screen->root; + return XCB_NONE; + } + + xcb_screen_t* X11::XCBDefaultScreen(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + return XCBScreenOfDisplay(connection, screen_nbr); + } + + int X11::XCBScreen(xcb_connection_t* connection) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + return screen_nbr; + } + + xcb_screen_t* X11::XCBScreenOfDisplay(xcb_connection_t* connection, int screenIndex) + { + NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server"); + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); + + for (; iter.rem; --screenIndex, xcb_screen_next (&iter)) + { + if (screenIndex == 0) + return iter.data; + } + + return nullptr; + } + + unsigned int X11::s_moduleReferenceCounter = 0; +} diff --git a/src/Nazara/Renderer/GLX/Display.hpp b/src/Nazara/Renderer/GLX/Display.hpp new file mode 100644 index 000000000..95161914f --- /dev/null +++ b/src/Nazara/Renderer/GLX/Display.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_X11DISPLAY_HPP +#define NAZARA_X11DISPLAY_HPP + +#include +#include +#include +#include +#include + +typedef struct _XCBKeySymbols xcb_key_symbols_t; + +namespace Nz +{ + class NAZARA_PLATFORM_API X11 + { + public: + X11() = delete; + ~X11() = delete; + + static bool CheckCookie(xcb_connection_t* connection, xcb_void_cookie_t cookie); + static void CloseConnection(xcb_connection_t* connection); + static void CloseEWMHConnection(xcb_ewmh_connection_t* ewmh_connection); + + static xcb_atom_t GetAtom(const std::string& name, bool onlyIfExists = false); + + static bool Initialize(); + static bool IsInitialized(); + + static xcb_key_symbols_t* XCBKeySymbolsAlloc(xcb_connection_t* connection); + static void XCBKeySymbolsFree(xcb_key_symbols_t* keySymbols); + + static xcb_connection_t* OpenConnection(); + static xcb_ewmh_connection_t* OpenEWMHConnection(xcb_connection_t* connection); + + static void Uninitialize(); + + static xcb_screen_t* XCBDefaultScreen(xcb_connection_t* connection); + static xcb_window_t XCBDefaultRootWindow(xcb_connection_t* connection); + static int XCBScreen(xcb_connection_t* connection); + static xcb_screen_t* XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr); + + private: + static unsigned int s_moduleReferenceCounter; + }; +} + +#endif // NAZARA_X11DISPLAY_HPP diff --git a/src/Nazara/Renderer/GLX/ScopedXCB.cpp b/src/Nazara/Renderer/GLX/ScopedXCB.cpp new file mode 100644 index 000000000..34725ec01 --- /dev/null +++ b/src/Nazara/Renderer/GLX/ScopedXCB.cpp @@ -0,0 +1,199 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + /*********************************************** + ScopedXCBConnection + ***********************************************/ + + ScopedXCBConnection::ScopedXCBConnection() : + m_connection(nullptr) + { + m_connection = X11::OpenConnection(); + } + + ScopedXCBConnection::~ScopedXCBConnection() + { + X11::CloseConnection(m_connection); + } + + ScopedXCBConnection::operator xcb_connection_t*() const + { + return m_connection; + } + + /*********************************************** + ScopedXCBEWMHConnection + ***********************************************/ + + ScopedXCBEWMHConnection::ScopedXCBEWMHConnection(xcb_connection_t* connection) : + m_ewmhConnection(nullptr) + { + m_ewmhConnection = X11::OpenEWMHConnection(connection); + } + + ScopedXCBEWMHConnection::~ScopedXCBEWMHConnection() + { + X11::CloseEWMHConnection(m_ewmhConnection); + } + + xcb_ewmh_connection_t* ScopedXCBEWMHConnection::operator ->() const + { + return m_ewmhConnection; + } + + ScopedXCBEWMHConnection::operator xcb_ewmh_connection_t*() const + { + return m_ewmhConnection; + } + + /*********************************************** + XCBGContext + ***********************************************/ + + XCBGContext::XCBGContext(xcb_connection_t* connection) : + m_connection(connection), + m_gcontext(XCB_NONE) + { + NazaraAssert(connection, "Connection must have been established"); + } + + XCBGContext::~XCBGContext() + { + Destroy(); + } + + bool XCBGContext::Create(xcb_drawable_t drawable, uint32_t value_mask, const uint32_t* value_list) + { + NazaraAssert(m_gcontext == XCB_NONE, "Context must have been destroyed before or just created"); + + m_gcontext = xcb_generate_id(m_connection); + + return X11::CheckCookie( + m_connection, + xcb_create_gc( + m_connection, + m_gcontext, + drawable, + value_mask, + value_list + )); + } + + void XCBGContext::Destroy() + { + if (m_gcontext == XCB_NONE) + return; + + if (!X11::CheckCookie( + m_connection, + xcb_free_gc( + m_connection, + m_gcontext + )) + ) + NazaraError("Failed to free gcontext"); + + m_gcontext = XCB_NONE; + } + + XCBGContext::operator xcb_gcontext_t() const + { + return m_gcontext; + } + + /*********************************************** + XCBPixmap + ***********************************************/ + + XCBPixmap::XCBPixmap() : + m_connection(nullptr), + m_pixmap(XCB_NONE) + { + } + + XCBPixmap::XCBPixmap(xcb_connection_t* connection) : + m_connection(connection), + m_pixmap(XCB_NONE) + { + } + + XCBPixmap::~XCBPixmap() + { + Destroy(); + } + + void XCBPixmap::Connect(xcb_connection_t* connection) + { + NazaraAssert(connection && !m_connection, "Connection must be established"); + + m_connection = connection; + } + + bool XCBPixmap::Create(uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height) + { + NazaraAssert(m_pixmap == XCB_NONE, "Pixmap must have been destroyed before or just created"); + + m_pixmap = xcb_generate_id(m_connection); + + return X11::CheckCookie( + m_connection, + xcb_create_pixmap( + m_connection, + depth, + m_pixmap, + drawable, + width, + height + )); + } + + bool XCBPixmap::CreatePixmapFromBitmapData(xcb_drawable_t drawable, uint8_t* data, uint32_t width, uint32_t height, uint32_t depth, uint32_t fg, uint32_t bg, xcb_gcontext_t* gcp) + { + NazaraAssert(m_pixmap == XCB_NONE, "Pixmap must have been destroyed before or just created"); + + m_pixmap = xcb_create_pixmap_from_bitmap_data( + m_connection, + drawable, + data, + width, + height, + depth, + fg, + bg, + gcp + ); + + return m_pixmap != XCB_NONE; + } + + void XCBPixmap::Destroy() + { + if (m_pixmap == XCB_NONE) + return; + + if (!X11::CheckCookie( + m_connection, + xcb_free_pixmap( + m_connection, + m_pixmap + )) + ) + NazaraError("Failed to free pixmap"); + + m_pixmap = XCB_NONE; + } + + XCBPixmap::operator xcb_pixmap_t() const + { + return m_pixmap; + } +} diff --git a/src/Nazara/Renderer/GLX/ScopedXCB.hpp b/src/Nazara/Renderer/GLX/ScopedXCB.hpp new file mode 100644 index 000000000..5070cc666 --- /dev/null +++ b/src/Nazara/Renderer/GLX/ScopedXCB.hpp @@ -0,0 +1,101 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SCOPEDXCB_HPP +#define NAZARA_SCOPEDXCB_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class ScopedXCBConnection + { + public: + ScopedXCBConnection(); + ~ScopedXCBConnection(); + + operator xcb_connection_t*() const; + + private: + xcb_connection_t* m_connection; + }; + + class ScopedXCBEWMHConnection + { + public: + ScopedXCBEWMHConnection(xcb_connection_t* connection); + ~ScopedXCBEWMHConnection(); + + xcb_ewmh_connection_t* operator ->() const; + + operator xcb_ewmh_connection_t*() const; + + private: + xcb_ewmh_connection_t* m_ewmhConnection; + }; + + template + class ScopedXCB + { + public: + ScopedXCB(T* pointer); + ~ScopedXCB(); + + T* operator ->() const; + T** operator &(); + + operator bool() const; + + T* get() const; + + private: + T* m_pointer; + }; + + class XCBGContext + { + public: + XCBGContext(xcb_connection_t* connection); + ~XCBGContext(); + + bool Create(xcb_drawable_t drawable, uint32_t value_mask, const uint32_t* value_list); + + void Destroy(); + + operator xcb_gcontext_t() const; + + private: + xcb_connection_t* m_connection; + xcb_gcontext_t m_gcontext; + }; + + class XCBPixmap + { + public: + XCBPixmap(); + XCBPixmap(xcb_connection_t* connection); + ~XCBPixmap(); + + void Connect(xcb_connection_t* connection); + bool Create(uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height); + bool CreatePixmapFromBitmapData(xcb_drawable_t drawable, uint8_t* data, uint32_t width, uint32_t height, uint32_t depth, uint32_t fg, uint32_t bg, xcb_gcontext_t* gcp); + + void Destroy(); + + operator xcb_pixmap_t() const; + + private: + xcb_connection_t* m_connection; + xcb_pixmap_t m_pixmap; + }; +} + +#include + +#endif // NAZARA_SCOPEDXCB_HPP diff --git a/src/Nazara/Renderer/GLX/ScopedXCB.inl b/src/Nazara/Renderer/GLX/ScopedXCB.inl new file mode 100644 index 000000000..b25ae1178 --- /dev/null +++ b/src/Nazara/Renderer/GLX/ScopedXCB.inl @@ -0,0 +1,46 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +namespace Nz +{ + template + ScopedXCB::ScopedXCB(T* pointer) : + m_pointer(pointer) + { + } + + template + ScopedXCB::~ScopedXCB() + { + std::free(m_pointer); + } + + template + T* ScopedXCB::operator ->() const + { + return m_pointer; + } + + template + T** ScopedXCB::operator &() + { + return &m_pointer; + } + + template + ScopedXCB::operator bool() const + { + return m_pointer != nullptr; + } + + template + T* ScopedXCB::get() const + { + return m_pointer; + } +} + +#include