#include "WaylandConnection.hpp" #include "cursor-shape-v1-client-protocol.h" #include "fractional-scale-v1-client-protocol.h" #include "tablet-v2-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h" #include "viewporter-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" #include #include #include #include #include namespace tp { struct WaylandRegistryListener { static void global(void* data, wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { static_cast(data)->bindGlobal(registry, name, interface, version); } static void globalRemove(void*, wl_registry*, uint32_t) {} static void ping(void*, xdg_wm_base* windowManager, uint32_t serial) { xdg_wm_base_pong(windowManager, serial); } static void outputGeometry(void*, wl_output*, int32_t, int32_t, int32_t, int32_t, int32_t, const char*, const char*, int32_t) {} static void outputMode(void* data, wl_output*, uint32_t flags, int32_t, int32_t, int32_t refresh) { if (!(flags & WL_OUTPUT_MODE_CURRENT) || refresh <= 0) return; static_cast(data)->mRefreshMillihertz = refresh; } static void outputDone(void*, wl_output*) {} static void outputScale(void*, wl_output*, int32_t) {} static void outputName(void*, wl_output*, const char*) {} static void outputDescription(void*, wl_output*, const char*) {} }; namespace { const wl_registry_listener kRegistryListener = { WaylandRegistryListener::global, WaylandRegistryListener::globalRemove, }; const xdg_wm_base_listener kWindowManagerListener = { WaylandRegistryListener::ping }; const wl_output_listener kOutputListener = { WaylandRegistryListener::outputGeometry, WaylandRegistryListener::outputMode, WaylandRegistryListener::outputDone, WaylandRegistryListener::outputScale, WaylandRegistryListener::outputName, WaylandRegistryListener::outputDescription, }; template Interface* bindAs(wl_registry* registry, uint32_t name, const wl_interface* interface, uint32_t offered, uint32_t wanted) { return static_cast(wl_registry_bind(registry, name, interface, std::min(offered, wanted))); } } WaylandConnection::WaylandConnection() { mDisplay = wl_display_connect(nullptr); if (!mDisplay) throw std::runtime_error("cannot connect to the Wayland display; this build supports Wayland only"); mRegistry = wl_display_get_registry(mDisplay); wl_registry_add_listener(mRegistry, &kRegistryListener, this); wl_display_roundtrip(mDisplay); wl_display_roundtrip(mDisplay); if (!mCompositor || !mWindowManager) throw std::runtime_error("Wayland compositor lacks wl_compositor or xdg_wm_base"); } WaylandConnection::~WaylandConnection() { if (mDecorationManager) zxdg_decoration_manager_v1_destroy(mDecorationManager); if (mTextInputManager) zwp_text_input_manager_v3_destroy(mTextInputManager); if (mTabletManager) zwp_tablet_manager_v2_destroy(mTabletManager); if (mCursorShapeManager) wp_cursor_shape_manager_v1_destroy(mCursorShapeManager); if (mFractionalScaleManager) wp_fractional_scale_manager_v1_destroy(mFractionalScaleManager); if (mViewporter) wp_viewporter_destroy(mViewporter); if (mDataDeviceManager) wl_data_device_manager_destroy(mDataDeviceManager); if (mOutput) wl_output_release(mOutput); if (mSeat) wl_seat_release(mSeat); if (mWindowManager) xdg_wm_base_destroy(mWindowManager); if (mCompositor) wl_compositor_destroy(mCompositor); if (mRegistry) wl_registry_destroy(mRegistry); if (mDisplay) { wl_display_flush(mDisplay); wl_display_disconnect(mDisplay); } } void WaylandConnection::bindGlobal(wl_registry* registry, uint32_t name, const std::string& interface, uint32_t version) { if (interface == wl_compositor_interface.name) { mCompositor = bindAs(registry, name, &wl_compositor_interface, version, 4); } else if (interface == xdg_wm_base_interface.name) { mWindowManager = bindAs(registry, name, &xdg_wm_base_interface, version, 5); xdg_wm_base_add_listener(mWindowManager, &kWindowManagerListener, this); } else if (interface == wl_seat_interface.name && mSeatName == 0) { mSeatName = name; mSeatVersion = version; } else if (interface == wl_data_device_manager_interface.name) { mDataDeviceManager = bindAs(registry, name, &wl_data_device_manager_interface, version, 3); } else if (interface == wp_viewporter_interface.name) { mViewporter = bindAs(registry, name, &wp_viewporter_interface, version, 1); } else if (interface == wp_fractional_scale_manager_v1_interface.name) { mFractionalScaleManager = bindAs(registry, name, &wp_fractional_scale_manager_v1_interface, version, 1); } else if (interface == wp_cursor_shape_manager_v1_interface.name) { mCursorShapeManager = bindAs(registry, name, &wp_cursor_shape_manager_v1_interface, version, 1); } else if (interface == zwp_text_input_manager_v3_interface.name) { mTextInputManager = bindAs(registry, name, &zwp_text_input_manager_v3_interface, version, 1); } else if (interface == zxdg_decoration_manager_v1_interface.name) { mDecorationManager = bindAs(registry, name, &zxdg_decoration_manager_v1_interface, version, 1); } else if (interface == zwp_tablet_manager_v2_interface.name) { mTabletManager = bindAs(registry, name, &zwp_tablet_manager_v2_interface, version, 1); } else if (interface == wl_output_interface.name && !mOutput) { mOutput = bindAs(registry, name, &wl_output_interface, version, 4); wl_output_add_listener(mOutput, &kOutputListener, this); } } wl_seat* WaylandConnection::bindSeat(const void* listener, void* data) { if (mSeat || mSeatName == 0) return mSeat; mSeat = bindAs(mRegistry, mSeatName, &wl_seat_interface, mSeatVersion, 8); wl_seat_add_listener(mSeat, static_cast(listener), data); return mSeat; } bool WaylandConnection::dispatch(int timeoutMs) { if (wl_display_dispatch_pending(mDisplay) > 0) { flush(); return true; } while (wl_display_prepare_read(mDisplay) != 0) { if (wl_display_dispatch_pending(mDisplay) > 0) return true; } flush(); pollfd descriptor = { wl_display_get_fd(mDisplay), POLLIN, 0 }; const int ready = poll(&descriptor, 1, timeoutMs); if (ready <= 0) { wl_display_cancel_read(mDisplay); return false; } if (wl_display_read_events(mDisplay) < 0) throw std::runtime_error("lost the Wayland connection"); wl_display_dispatch_pending(mDisplay); flush(); return true; } void WaylandConnection::roundtrip() { wl_display_roundtrip(mDisplay); } void WaylandConnection::flush() { while (wl_display_flush(mDisplay) < 0 && errno == EAGAIN) { pollfd descriptor = { wl_display_get_fd(mDisplay), POLLOUT, 0 }; poll(&descriptor, 1, -1); } } }