#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; };