diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6d3a5e..398f9a3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -20,8 +20,12 @@ list(APPEND core_LIB_INCLUDES file_utilities/BinaryFile.cpp file_utilities/File.cpp file_utilities/FileFormats.cpp + memory/SharedMemory.cpp + RandomUtils.cpp StringUtils.cpp - http/HttpResponse.cpp) + http/HttpResponse.cpp + http/HttpHeader.cpp + http/HttpRequest.cpp) # add the executable add_library(core SHARED ${core_LIB_INCLUDES} ${core_HEADERS}) @@ -30,6 +34,7 @@ target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/file_utilities" "${CMAKE_CURRENT_SOURCE_DIR}/loggers" + "${CMAKE_CURRENT_SOURCE_DIR}/memory" ) set_target_properties( core PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_property(TARGET core PROPERTY FOLDER src) \ No newline at end of file diff --git a/src/core/RandomUtils.cpp b/src/core/RandomUtils.cpp new file mode 100644 index 0000000..fa31fab --- /dev/null +++ b/src/core/RandomUtils.cpp @@ -0,0 +1,22 @@ +#include "RandomUtils.h" + +#include +#include +#include + +std::vector RandomUtils::getRandomVecUnsigned(std::size_t size) +{ + std::random_device rnd_device; + + std::mt19937 mersenne_engine {rnd_device()}; + std::uniform_int_distribution dist {0, 9}; + + auto generator = [&dist, &mersenne_engine]() + { + return dist(mersenne_engine); + }; + + std::vector vec(size); + std::generate(std::begin(vec), std::end(vec), generator); + return vec; +} diff --git a/src/core/RandomUtils.h b/src/core/RandomUtils.h new file mode 100644 index 0000000..062bd34 --- /dev/null +++ b/src/core/RandomUtils.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class RandomUtils +{ +public: + + static std::vector getRandomVecUnsigned(std::size_t size); +}; diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index fff9b36..757895c 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -13,6 +13,7 @@ public: static constexpr char EQUALS = '='; static constexpr char DOUBLE_QUOTE = '"'; static constexpr char SINGLE_QUOTE = '\''; + static constexpr char COLON = ':'; static bool IsAlphaNumeric(char c); static bool IsSpace(char c); diff --git a/src/core/http/HttpHeader.cpp b/src/core/http/HttpHeader.cpp new file mode 100644 index 0000000..c841cca --- /dev/null +++ b/src/core/http/HttpHeader.cpp @@ -0,0 +1,80 @@ +#include "HttpHeader.h" +#include "StringUtils.h" + + +void HttpHeader::parse(const std::vector& message) +{ + std::string tag; + std::string value; + bool foundDelimiter{false}; + for (const auto& line : message) + { + for(std::size_t idx = 0; idx< line.size(); idx++) + { + const auto c = line[idx]; + if (c == StringUtils::COLON) + { + foundDelimiter = true; + } + else if(foundDelimiter) + { + value.push_back(c); + } + else + { + tag.push_back(c); + } + } + } + + if (tag.empty() || value.empty()) + { + return; + } + + if (tag == "Host") + { + mHost = value; + } + else if (tag == "User-Agent") + { + mUserAgent = value; + } + else if (tag == "Accept") + { + mAccept = value; + } + else if (tag == "Accept-Language") + { + mAcceptLanguage = value; + } + else if (tag == "Accept-Encoding") + { + mAcceptEncoding = value; + } + else if (tag == "Connection") + { + mConnection = value; + } + else if (tag == "Referer") + { + mReferer = value; + } + else if (tag == "Sec-Fetch-Dest") + { + mSecFetchDest = value; + } + else if (tag == "Sec-Fetch-Mode") + { + mSecFetchMode = value; + } + else if (tag == "Sec-Fetch-Site") + { + mSecFetchSite = value; + } + else + { + mOtherFields[tag] = value; + } + +} diff --git a/src/core/http/HttpHeader.h b/src/core/http/HttpHeader.h new file mode 100644 index 0000000..2ce412c --- /dev/null +++ b/src/core/http/HttpHeader.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +class HttpHeader +{ +public: + void parse(const std::vector& message); + +private: + + std::string mHost; + std::string mUserAgent; + std::string mAccept; + std::string mAcceptLanguage; + std::string mAcceptEncoding; + std::string mConnection; + std::string mReferer; + std::string mSecFetchDest; + std::string mSecFetchMode; + std::string mSecFetchSite; + + std::map mOtherFields; +}; diff --git a/src/core/http/HttpRequest.cpp b/src/core/http/HttpRequest.cpp new file mode 100644 index 0000000..d2d5e42 --- /dev/null +++ b/src/core/http/HttpRequest.cpp @@ -0,0 +1,68 @@ +#include "HttpRequest.h" + +#include "StringUtils.h" + +#include + +void HttpRequest::parseMessage(const std::string& message) +{ + std::stringstream ss(message); + + std::string buffer; + bool firstLine {true}; + + std::vector headers; + while(std::getline(ss, buffer, '\n')) + { + if (firstLine) + { + parseFirstLine(buffer); + firstLine = false; + } + else + { + headers.push_back(buffer); + } + } + mHeader.parse(headers); +} + +void HttpRequest::parseFirstLine(const std::string& line) +{ + bool inPath{false}; + bool inMethod{true}; + bool inProtocol{false}; + + for (std::size_t idx=0; idx + +class HttpRequest +{ +public: + HttpRequest() = default; + + void parseMessage(const std::string& message); + +private: + + void parseFirstLine(const std::string& line); + + HttpHeader mHeader; + std::string mMethod; + std::string mPath; + std::string mProtocolVersion; +}; diff --git a/src/core/http/HttpResponse.h b/src/core/http/HttpResponse.h index 6e11226..e6e97bc 100644 --- a/src/core/http/HttpResponse.h +++ b/src/core/http/HttpResponse.h @@ -5,11 +5,6 @@ class HttpResponse { - std::string mHttpVersion; - std::string mResponseCode; - std::string mContentType; - std::string mBody; - public: HttpResponse(); @@ -23,4 +18,10 @@ public: std::string GetHeaderString() const; std::string ToString() const; + +private: + std::string mHttpVersion; + std::string mResponseCode; + std::string mContentType; + std::string mBody; }; diff --git a/src/core/memory/SharedMemory.cpp b/src/core/memory/SharedMemory.cpp new file mode 100644 index 0000000..8fcf816 --- /dev/null +++ b/src/core/memory/SharedMemory.cpp @@ -0,0 +1,68 @@ +#include "SharedMemory.h" + +#include "RandomUtils.h" + +#include +#include +#include +#include +#include + +void SharedMemory::allocate(const std::string& namePrefix, std::size_t size) +{ + createFile(namePrefix); + + if (!mIsValid) + { + return; + } + + int ret{-1}; + do { + ret = ftruncate(mFileDescriptor, size); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + { + close(mFileDescriptor); + mIsValid = false; + } +} + +int SharedMemory::getFileDescriptor() const +{ + return mFileDescriptor; +} + +bool SharedMemory::isValid() const +{ + return mIsValid; +} + +void SharedMemory::createFile(const std::string& namePrefix) +{ + unsigned retries = 100; + do { + const auto name = getRandomName(namePrefix); + --retries; + + const int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) + { + shm_unlink(name.c_str()); + mFileDescriptor = fd; + mIsValid = true; + break; + } + } while (retries > 0 && errno == EEXIST); +} + +std::string SharedMemory::getRandomName(const std::string& namePrefix) const +{ + std::string randomSuffix; + for (const auto entry : RandomUtils::getRandomVecUnsigned(6)) + { + randomSuffix += std::to_string(entry); + } + return namePrefix + randomSuffix; +} diff --git a/src/core/memory/SharedMemory.h b/src/core/memory/SharedMemory.h new file mode 100644 index 0000000..333d6b6 --- /dev/null +++ b/src/core/memory/SharedMemory.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class SharedMemory +{ +public: + void allocate(const std::string& namePrefix, std::size_t size); + + int getFileDescriptor() const; + + bool isValid() const; + +private: + + void createFile(const std::string& namePrefix); + + std::string getRandomName(const std::string& namePrefix) const; + + int mFileDescriptor{0}; + bool mIsValid{false}; +}; diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index c1ae972..60a8125 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -8,11 +8,13 @@ list(APPEND network_HEADERS NetworkManager.h sockets/Socket.h sockets/SocketInterface.h + sockets/ISocketMessageHandler.h ) list(APPEND network_LIB_INCLUDES NetworkManager.cpp sockets/Socket.cpp + web/HttpMessageHandler.cpp ) add_library(network SHARED ${network_LIB_INCLUDES} ${platform_INCLUDES} ${network_HEADERS}) @@ -20,6 +22,7 @@ add_library(network SHARED ${network_LIB_INCLUDES} ${platform_INCLUDES} ${networ target_include_directories(network PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/sockets" + "${CMAKE_CURRENT_SOURCE_DIR}/web" "${PROJECT_SOURCE_DIR}/src/core/http" ) set_target_properties( network PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) diff --git a/src/network/NetworkManager.cpp b/src/network/NetworkManager.cpp index 0eb58a8..4507e4b 100644 --- a/src/network/NetworkManager.cpp +++ b/src/network/NetworkManager.cpp @@ -3,6 +3,8 @@ #include "UnixSocketInterface.h" #endif +#include + NetworkManager::NetworkManager() : mActiveSockets(), mSocketInterface() @@ -29,6 +31,7 @@ void NetworkManager::Initialize() void NetworkManager::RunHttpServer() { + std::cout << "Running http server" << std::endl; if (!mSocketInterface) { Initialize(); @@ -40,6 +43,19 @@ void NetworkManager::RunHttpServer() mSocketInterface->Run(socket); } +void NetworkManager::RunHttpClient() +{ + std::cout << "Running http client" << std::endl; + if (!mSocketInterface) + { + Initialize(); + } + + auto socket = Socket::Create(); + mSocketInterface->InitializeSocket(socket, "127.0.0.1"); + mSocketInterface->Write(socket, "Hello Friend"); +} + void NetworkManager::ShutDown() { diff --git a/src/network/NetworkManager.h b/src/network/NetworkManager.h index bd4e05d..92deef5 100644 --- a/src/network/NetworkManager.h +++ b/src/network/NetworkManager.h @@ -20,11 +20,14 @@ public: void RunHttpServer(); + void RunHttpClient(); + void ShutDown(); private: std::vector mActiveSockets; ISocketInterfaceUPtr mSocketInterface; + }; using NetworkManagerUPtr = std::unique_ptr; diff --git a/src/network/sockets/ISocketMessageHandler.h b/src/network/sockets/ISocketMessageHandler.h new file mode 100644 index 0000000..5cab10b --- /dev/null +++ b/src/network/sockets/ISocketMessageHandler.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class ISocketMessageHandler +{ +public: + virtual ~ISocketMessageHandler() = default; + virtual std::string onMessage(const std::string& message) = 0; +}; diff --git a/src/network/sockets/Socket.cpp b/src/network/sockets/Socket.cpp index f273021..d8f8e74 100644 --- a/src/network/sockets/Socket.cpp +++ b/src/network/sockets/Socket.cpp @@ -47,3 +47,14 @@ unsigned Socket::GetPort() const { return mPort; } + +std::string Socket::getAddress() const +{ + return mAddress; +} + + +void Socket::setAddress(const std::string& address) +{ + mAddress = address; +} diff --git a/src/network/sockets/Socket.h b/src/network/sockets/Socket.h index f6756e1..2e4e76d 100644 --- a/src/network/sockets/Socket.h +++ b/src/network/sockets/Socket.h @@ -26,11 +26,15 @@ public: void SetMessage(const std::string& message); + std::string getAddress() const; + void setAddress(const std::string& address); + private: SocketHandle mHandle; unsigned mPort{0}; std::string mMessage; + std::string mAddress; }; using SocketPtr = std::unique_ptr; diff --git a/src/network/sockets/SocketInterface.h b/src/network/sockets/SocketInterface.h index d5046b3..6bd2d33 100644 --- a/src/network/sockets/SocketInterface.h +++ b/src/network/sockets/SocketInterface.h @@ -13,9 +13,11 @@ public: virtual ~ISocketInterface() = default; - virtual void InitializeSocket(const SocketPtr& socket) = 0; + virtual void InitializeSocket(const SocketPtr& socket, const std::string& address = {}) = 0; virtual void Listen(const SocketPtr& socket) = 0; virtual void Run(const SocketPtr& socket) = 0; + + virtual void Write(const SocketPtr& socket, const std::string& message) = 0; }; using ISocketInterfaceUPtr = std::unique_ptr; diff --git a/src/network/sockets/UnixSocketInterface.cpp b/src/network/sockets/UnixSocketInterface.cpp index 5a77ff6..3ad9992 100644 --- a/src/network/sockets/UnixSocketInterface.cpp +++ b/src/network/sockets/UnixSocketInterface.cpp @@ -2,17 +2,20 @@ #include "HttpResponse.h" +#include "HttpMessageHandler.h" + #include #include #include #include #include #include +#include UnixSocketInterface::UnixSocketInterface() : mBufferSize(1024) { - + mMessageHandler = std::make_unique(); } std::unique_ptr UnixSocketInterface::Create() @@ -20,10 +23,61 @@ std::unique_ptr UnixSocketInterface::Create() return std::make_unique(); } -void UnixSocketInterface::InitializeSocket(const SocketPtr& socketPtr) +UnixSocketInterface::~UnixSocketInterface() { - auto handle = socket(AF_INET, SOCK_STREAM, 0); + +} + +void UnixSocketInterface::InitializeSocket(const SocketPtr& socketPtr, const std::string& address) +{ + auto handle = ::socket(AF_INET, SOCK_STREAM, 0); socketPtr->SetHandle(handle); + + if (!address.empty()) + { + socketPtr->setAddress(address); + } +} + +void UnixSocketInterface::Write(const SocketPtr& socket, const std::string& message) +{ + if(socket->GetHandle() < 0) + { + std::cerr << "Error opening socket" << std::endl; + return; + } + + const auto port = static_cast(socket->GetPort()); + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + + inet_aton(socket->getAddress().c_str(), &serv_addr.sin_addr); + serv_addr.sin_port = htons(port); + + int result = connect(socket->GetHandle(), (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if(result< 0) + { + std::cerr << "Error connecting to socket" << std::endl; + return; + } + + auto n = write(socket->GetHandle(), message.c_str(), message.length()); + if (n < 0) + { + std::cerr << "Error on write" << std::endl; + return; + } + + char buffer[mBufferSize] = {0}; + int res = read(socket->GetHandle(), buffer, mBufferSize); + if (res < 0) + { + std::cerr << "Error on read" << std::endl; + return; + } + socket->SetMessage(buffer); + std::cout << "Here is the message: " << buffer << std::endl; } void UnixSocketInterface::Listen(const SocketPtr& socket) @@ -34,7 +88,7 @@ void UnixSocketInterface::Listen(const SocketPtr& socket) return; } - int port = static_cast(socket->GetPort()); + const auto port = static_cast(socket->GetPort()); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; @@ -63,8 +117,7 @@ void UnixSocketInterface::Run(const SocketPtr& socket) socklen_t clilen = sizeof(cli_addr); while(true) { - auto new_socket_handle = accept(socket->GetHandle(), - (struct sockaddr *) &cli_addr, &clilen); + const auto new_socket_handle = accept(socket->GetHandle(), (struct sockaddr *) &cli_addr, &clilen); if (new_socket_handle < 0) { std::cerr << "Error on accept" << std::endl; @@ -81,11 +134,8 @@ void UnixSocketInterface::Run(const SocketPtr& socket) socket->SetMessage(buffer); std::cout << "Here is the message: " << buffer << std::endl; - HttpResponse response; - response.SetBody("Hello world!"); - - auto response_message = response.ToString(); - n = write(new_socket_handle, response_message.c_str(), response_message.length()); + const auto response = mMessageHandler->onMessage(buffer); + n = write(new_socket_handle, response.c_str(), response.length()); if (n < 0) { std::cerr << "Error on write" << std::endl; diff --git a/src/network/sockets/UnixSocketInterface.h b/src/network/sockets/UnixSocketInterface.h index 829ae8a..fd33649 100644 --- a/src/network/sockets/UnixSocketInterface.h +++ b/src/network/sockets/UnixSocketInterface.h @@ -6,6 +6,8 @@ #include #include +class ISocketMessageHandler; + class UnixSocketInterface : public ISocketInterface { @@ -13,19 +15,22 @@ public: UnixSocketInterface(); - virtual ~UnixSocketInterface() = default; + virtual ~UnixSocketInterface(); static std::unique_ptr Create(); - void InitializeSocket(const SocketPtr& socket) override; + void InitializeSocket(const SocketPtr& socket, const std::string& address = {}) override; void Listen(const SocketPtr& socket) override; + void Write(const SocketPtr& socket, const std::string& message) override; + void Run(const SocketPtr& socket) override; private: std::size_t mBufferSize { 0 }; + std::unique_ptr mMessageHandler; }; using UnixSocketInterfacePtr = std::unique_ptr; diff --git a/src/network/web/HttpMessageHandler.cpp b/src/network/web/HttpMessageHandler.cpp new file mode 100644 index 0000000..a3ca4ff --- /dev/null +++ b/src/network/web/HttpMessageHandler.cpp @@ -0,0 +1,17 @@ +#include "HttpMessageHandler.h" + +#include "HttpRequest.h" +#include "HttpResponse.h" + +std::string HttpMessageHandler::onMessage(const std::string& message) +{ + HttpRequest request; + request.parseMessage(message); + + HttpResponse response; + response.SetBody("Hello world!"); + + const auto response_message = response.ToString(); + + return response_message; +} diff --git a/src/network/web/HttpMessageHandler.h b/src/network/web/HttpMessageHandler.h new file mode 100644 index 0000000..60d3e17 --- /dev/null +++ b/src/network/web/HttpMessageHandler.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ISocketMessageHandler.h" + +class HttpMessageHandler : public ISocketMessageHandler +{ +public: + std::string onMessage(const std::string& message) override; +}; diff --git a/src/windows/CMakeLists.txt b/src/windows/CMakeLists.txt index 302a948..4c813fb 100644 --- a/src/windows/CMakeLists.txt +++ b/src/windows/CMakeLists.txt @@ -11,10 +11,36 @@ list(APPEND platform_INCLUDES ui_interfaces/x11/XcbLayerInterface.cpp ui_interfaces/x11/XcbTextInterface.cpp ui_interfaces/x11/XcbKeyboard.cpp - ui_interfaces/x11/GlxInterface.cpp) + ui_interfaces/x11/GlxInterface.cpp + + ui_interfaces/wayland/WaylandWindowInterface.cpp + ) list(APPEND platform_LIBS X11 X11-xcb xcb ) + + + find_path( + WAYLAND_CLIENT_INCLUDE_DIR + NAMES wayland-client.h + ) + + find_path( + WAYLAND_EXTENSIONS_INCLUDE_DIR + NAMES xdg-shell-client-protocol.h + HINTS ENV WAYLAND_EXTENSION_DIR + ) + + find_library( + WAYLAND_CLIENT_LIBRARY + NAMES wayland-client libwayland-client + ) + + list(APPEND platform_INCLUDES + ui_interfaces/win32/Win32UIInterface.h + ${WAYLAND_EXTENSIONS_INCLUDE_DIR}/xdg-shell-protocol.cpp + ) + else() list(APPEND platform_INCLUDES ui_interfaces/win32/Win32UIInterface.h @@ -38,15 +64,18 @@ target_include_directories(windows PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/managers" "${CMAKE_CURRENT_SOURCE_DIR}/ui_interfaces/x11" + "${CMAKE_CURRENT_SOURCE_DIR}/ui_interfaces/wayland" "${CMAKE_CURRENT_SOURCE_DIR}/ui_interfaces/win32" "${PROJECT_SOURCE_DIR}/src/core" "${PROJECT_SOURCE_DIR}/src/geometry" "${PROJECT_SOURCE_DIR}/src/graphics" "${PROJECT_SOURCE_DIR}/src/ui_elements" "${PROJECT_SOURCE_DIR}/src/ui_elements/widgets" + ${WAYLAND_CLIENT_INCLUDE_DIR} + ${WAYLAND_EXTENSIONS_INCLUDE_DIR} ) -target_link_libraries(windows PUBLIC ${platform_LIBS} core geometry graphics ui_elements) +target_link_libraries(windows PUBLIC ${platform_LIBS} core geometry graphics ui_elements ${WAYLAND_CLIENT_LIBRARY}) set_property(TARGET windows PROPERTY FOLDER src) set_target_properties( windows PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) \ No newline at end of file diff --git a/src/windows/ui_interfaces/wayland/WaylandWindowInterface.cpp b/src/windows/ui_interfaces/wayland/WaylandWindowInterface.cpp new file mode 100644 index 0000000..cd32d68 --- /dev/null +++ b/src/windows/ui_interfaces/wayland/WaylandWindowInterface.cpp @@ -0,0 +1 @@ +#include "WaylandWindowInterface.h" diff --git a/src/windows/ui_interfaces/wayland/WaylandWindowInterface.h b/src/windows/ui_interfaces/wayland/WaylandWindowInterface.h new file mode 100644 index 0000000..4b03501 --- /dev/null +++ b/src/windows/ui_interfaces/wayland/WaylandWindowInterface.h @@ -0,0 +1,263 @@ +#pragma once + +#include "wayland-client.h" +#include "xdg-shell-client-protocol.h" + +#include "Window.h" +#include "SharedMemory.h" + +#include +#include + +#include +#include + +#include + +class WaylandWindowInterface +{ + +public: + + WaylandWindowInterface() + { + auto registry_handle_global = [](void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) + { + auto thisClass = static_cast(data); + + printf("interface: '%s', version: %d, name: %d\n", interface, version, name); + + if (strcmp(interface, wl_compositor_interface.name) == 0) + { + thisClass->setCompositor(static_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 4))); + } + else if (strcmp(interface, wl_shm_interface.name) == 0) + { + thisClass->setSharedMemory(static_cast(wl_registry_bind(registry, name, &wl_shm_interface, 1))); + } + else if (strcmp(interface, xdg_wm_base_interface.name) == 0) + { + thisClass->setXdgBase(static_cast(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1))); + } + }; + + auto registry_handle_global_remove = [](void *data, struct wl_registry *registry, + uint32_t name) + { + // This space deliberately left blank; + }; + + mRegistryListener.global = registry_handle_global; + mRegistryListener.global_remove = registry_handle_global_remove; + } + + void setXdgBase(xdg_wm_base* xdg_base) + { + mXdgBase = xdg_base; + auto xdg_ping_handler = [](void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) + { + auto thisClass = static_cast(data); + thisClass->doXdgPong(serial); + }; + mXdgBaseListener.ping = xdg_ping_handler; + xdg_wm_base_add_listener(mXdgBase, &mXdgBaseListener, this); + } + + void doXdgPong(uint32_t serial) + { + xdg_wm_base_pong(mXdgBase, serial); + } + + void setCompositor(wl_compositor* compositor) + { + mCompositor = compositor; + } + + void setSharedMemory(wl_shm* shared_memory) + { + mWlSharedMemory = shared_memory; + } + + void connect() + { + mDisplay = wl_display_connect(nullptr); + + if (!mDisplay) + { + std::cout << "Display connect error" << std::endl; + } + + auto registry = wl_display_get_registry(mDisplay); + if (!registry) + { + std::cout << "Failed to get registry" << std::endl; + } + + wl_registry_add_listener(registry, &mRegistryListener, this); + wl_display_roundtrip(mDisplay); + } + + void createSurface() + { + mSurface = wl_compositor_create_surface(mCompositor); + mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface); + + auto xdg_surface_configure = [](void *data, struct xdg_surface *xdg_surface, uint32_t serial) + { + auto thisClass = static_cast(data); + thisClass->onXdgSurfaceConfigure(data, xdg_surface, serial); + }; + mXdgSurfaceListener.configure = xdg_surface_configure; + + xdg_surface_add_listener(mXdgSurface, &mXdgSurfaceListener, this); + + mXdgTopLevel = xdg_surface_get_toplevel(mXdgSurface); + xdg_toplevel_set_title(mXdgTopLevel, "Example client"); + + wl_surface_commit(mSurface); + } + + void onXdgSurfaceConfigure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) + { + xdg_surface_ack_configure(xdg_surface, serial); + + auto buffer = draw_frame(); + wl_surface_attach(mSurface, buffer, 0, 0); + //wl_surface_damage(mSurface, 0, 0, UINT32_MAX, UINT32_MAX); + wl_surface_commit(mSurface); + } + + wl_buffer* draw_frame() + { + const auto width = mWindow->GetWidth(); + const auto height = mWindow->GetHeight(); + + const int bitDepth = 4; + + const int stride = width * bitDepth; + //const int numBuffers = 2; // i.e. front/back + const int numBuffers = 1; + const int shm_pool_size = height * stride * numBuffers; + + initializeSharedBuffer(shm_pool_size); + + if (!mSharedMemory->isValid()) + { + std::cout << "Failed to allocate shared memory" << std::endl; + return nullptr; + } + + if (!mPoolData) + { + std::cout << "Failed to mmap pool" << std::endl; + return nullptr; + } + + auto pool = wl_shm_create_pool(mWlSharedMemory, mSharedMemory->getFileDescriptor(), shm_pool_size); + + int index = 0; + // int offset = height * stride * index; // Two buffers, offset to starting point of second + int offset = 0; + + mWorkingBuffer = wl_shm_pool_create_buffer(pool, offset, width, height, stride, WL_SHM_FORMAT_XRGB8888); + + wl_shm_pool_destroy(pool); + close(mSharedMemory->getFileDescriptor()); + + drawCheckerboard(width, height, offset); + + munmap(mPoolData, shm_pool_size); + + auto wl_buffer_release = [](void *data, struct wl_buffer *wl_buffer) + { + wl_buffer_destroy(wl_buffer); + }; + mBufferListener.release = wl_buffer_release; + + wl_buffer_add_listener(mWorkingBuffer, &mBufferListener, nullptr); + + return mWorkingBuffer; + } + + void drawCheckerboard(int width, int height, int offset) + { + uint32_t *pixels = (uint32_t *)&mPoolData[offset]; + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + if ((x + y / 8 * 8) % 16 < 8) + { + pixels[y * width + x] = 0xFF666666; + } + else + { + pixels[y * width + x] = 0xFFEEEEEE; + } + } + } + } + + void disconnect() + { + if (mDisplay) + { + wl_display_disconnect(mDisplay); + } + } + + void run() + { + while (wl_display_dispatch(mDisplay) != -1) { + /* This space deliberately left blank */ + } + } + + void initializeSharedBuffer(int size) + { + mSharedMemory = std::make_unique(); + mSharedMemory->allocate("/wl_shm-XXXXXX", size); + + if (!mSharedMemory->isValid()) + { + return; + } + mPoolData = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, mSharedMemory->getFileDescriptor(), 0)); + if (mPoolData == MAP_FAILED) + { + close(mSharedMemory->getFileDescriptor()); + mPoolData = nullptr; + } + } + + void addWindow(mt::Window* window) + { + mWindow = window; + } + +private: + + mt::Window* mWindow{nullptr}; + + wl_display* mDisplay{nullptr}; + wl_compositor* mCompositor{nullptr}; + wl_registry_listener mRegistryListener; + + xdg_wm_base* mXdgBase{nullptr}; + xdg_wm_base_listener mXdgBaseListener; + + wl_surface* mSurface{nullptr}; + xdg_surface* mXdgSurface{nullptr}; + xdg_surface_listener mXdgSurfaceListener{nullptr}; + + xdg_toplevel* mXdgTopLevel{nullptr}; + + wl_shm* mWlSharedMemory{nullptr}; + std::unique_ptr mSharedMemory; + uint8_t* mPoolData{nullptr}; + wl_shm_pool* mPool{nullptr}; + + wl_buffer* mWorkingBuffer{nullptr}; + wl_buffer_listener mBufferListener; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c8f4a82..bb9657b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,8 +15,11 @@ list(APPEND TestFiles graphics/TestRasterizer.cpp ipc/TestDbus.cpp image/TestPngWriter.cpp + network/TestNetworkManagerClient.cpp + network/TestNetworkManagerServer.cpp publishing/TestPdfWriter.cpp video/TestVideoDecoder.cpp + windows/TestWaylandWindow.cpp web/TestMarkdownParser.cpp web/TestXmlParser.cpp) @@ -28,8 +31,11 @@ list(APPEND TestNames TestRasterizer TestDbus TestPngWriter + TestNetworkManagerClient + TestNetworkManagerServer TestPdfWriter TestVideoDecoder + TestWaylandWindow TestMarkdownParser TestXmlParser) diff --git a/test/image/TestPngWriter.cpp b/test/image/TestPngWriter.cpp index 4dfbcb3..0786e0f 100644 --- a/test/image/TestPngWriter.cpp +++ b/test/image/TestPngWriter.cpp @@ -1,6 +1,8 @@ #include "Image.h" #include "PngWriter.h" +#include + int main() { unsigned width = 200; diff --git a/test/network/TestNetworkManagerClient.cpp b/test/network/TestNetworkManagerClient.cpp new file mode 100644 index 0000000..d2d0d0f --- /dev/null +++ b/test/network/TestNetworkManagerClient.cpp @@ -0,0 +1,12 @@ +#include "NetworkManager.h" + +#include + +int main() +{ + std::cout << "into main" << std::endl; + auto network_manager = NetworkManager::Create(); + + network_manager->RunHttpClient(); + return 0; +} diff --git a/test/network/TestNetworkManagerServer.cpp b/test/network/TestNetworkManagerServer.cpp new file mode 100644 index 0000000..4b33c35 --- /dev/null +++ b/test/network/TestNetworkManagerServer.cpp @@ -0,0 +1,12 @@ +#include "NetworkManager.h" + +#include + +int main() +{ + std::cout << "into main" << std::endl; + auto network_manager = NetworkManager::Create(); + + network_manager->RunHttpServer(); + return 0; +} diff --git a/test/windows/TestWaylandWindow.cpp b/test/windows/TestWaylandWindow.cpp new file mode 100644 index 0000000..b3d172d --- /dev/null +++ b/test/windows/TestWaylandWindow.cpp @@ -0,0 +1,23 @@ +#include "WaylandWindowInterface.h" + +#include "Window.h" + + + +int main() +{ + WaylandWindowInterface window_interface; + window_interface.connect(); + + auto window = mt::Window::Create(); + window->SetSize(800, 600); + + window_interface.addWindow(window.get()); + + window_interface.createSurface(); + + window_interface.run(); + + window_interface.disconnect(); + return 0; +}