Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for XDG Shell on Wayland #1190

Merged
merged 3 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[submodule "external/Vulkan-Headers"]
path = external/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "external/wayland-protocols"]
path = external/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
branch = main
1 change: 1 addition & 0 deletions external/wayland-protocols
Submodule wayland-protocols added at 7f2001
30 changes: 25 additions & 5 deletions framework/application/wayland_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct wl_keyboard_listener WaylandContext::keyboard_listener_;
struct wl_seat_listener WaylandContext::seat_listener_;
struct wl_registry_listener WaylandContext::registry_listener_;
struct wl_output_listener WaylandContext::output_listener_;
struct xdg_wm_base_listener WaylandContext::xdg_wm_base_listener_;

WaylandContext::WaylandContext(Application* application) : WsiContext(application)
{
Expand Down Expand Up @@ -65,6 +66,8 @@ WaylandContext::WaylandContext(Application* application) : WsiContext(applicatio
output_listener_.done = HandleOutputDone;
output_listener_.scale = HandleOutputScale;

xdg_wm_base_listener_.ping = HandleXdgWmBasePing;

success = wayland_loader_.Initialize();
auto& wl = wayland_loader_.GetFunctionTable();

Expand Down Expand Up @@ -99,7 +102,7 @@ WaylandContext::WaylandContext(Application* application) : WsiContext(applicatio
GFXRECON_LOG_ERROR("Failed to bind Wayland compositor");
success = false;
}
else if (shell_ == nullptr)
else if (shell_ == nullptr && xdg_wm_base_ == nullptr)
{
GFXRECON_LOG_ERROR("Failed to bind Wayland shell");
success = false;
Expand Down Expand Up @@ -135,6 +138,11 @@ WaylandContext::~WaylandContext()
wl.shell_destroy(shell_);
}

if (xdg_wm_base_)
{
wl.xdg->xdg_wm_base_destroy(xdg_wm_base_);
}

if (compositor_)
{
wl.compositor_destroy(compositor_);
Expand Down Expand Up @@ -199,22 +207,28 @@ void WaylandContext::HandleRegistryGlobal(
{
auto wayland_context = reinterpret_cast<WaylandContext*>(data);
auto& wl = wayland_context->GetWaylandFunctionTable();
if (util::platform::StringCompare(interface, "wl_compositor") == 0)
if (util::platform::StringCompare(interface, wl.compositor_interface->name) == 0)
{
// wl_compositor needs to support wl_surface::set_buffer_scale request
wayland_context->compositor_ = reinterpret_cast<wl_compositor*>(
wl.registry_bind(registry, id, wl.compositor_interface, WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION));
}
else if (util::platform::StringCompare(interface, "wl_shell") == 0)
else if (util::platform::StringCompare(interface, wl.shell_interface->name) == 0)
{
wayland_context->shell_ = reinterpret_cast<wl_shell*>(wl.registry_bind(registry, id, wl.shell_interface, 1));
}
else if (util::platform::StringCompare(interface, "wl_seat") == 0)
else if (util::platform::StringCompare(interface, wl.xdg->xdg_wm_base_interface.name) == 0)
{
wayland_context->xdg_wm_base_ =
reinterpret_cast<xdg_wm_base*>(wl.registry_bind(registry, id, &wl.xdg->xdg_wm_base_interface, 1));
wl.xdg->xdg_wm_base_add_listener(wayland_context->xdg_wm_base_, &xdg_wm_base_listener_, wayland_context);
}
else if (util::platform::StringCompare(interface, wl.seat_interface->name) == 0)
{
wayland_context->seat_ = reinterpret_cast<wl_seat*>(wl.registry_bind(registry, id, wl.seat_interface, 1));
wl.seat_add_listener(wayland_context->seat_, &seat_listener_, wayland_context);
}
else if (util::platform::StringCompare(interface, "wl_output") == 0)
else if (util::platform::StringCompare(interface, wl.output_interface->name) == 0)
{
// wl_output needs to support wl_output::scale event
auto output = reinterpret_cast<wl_output*>(
Expand Down Expand Up @@ -398,5 +412,11 @@ void WaylandContext::HandleOutputScale(void* data, struct wl_output* wl_output,
output_info.scale = factor;
}

void WaylandContext::HandleXdgWmBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial)
{
auto& wl = reinterpret_cast<WaylandContext*>(data)->GetWaylandFunctionTable();
wl.xdg->xdg_wm_base_pong(xdg_wm_base, serial);
}

GFXRECON_END_NAMESPACE(application)
GFXRECON_END_NAMESPACE(gfxrecon)
6 changes: 6 additions & 0 deletions framework/application/wayland_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class WaylandContext : public WsiContext

struct wl_shell* GetShell() const { return shell_; }

struct xdg_wm_base* GetXdgWmBase() const { return xdg_wm_base_; }

struct wl_compositor* GetCompositor() const { return compositor_; }

const OutputInfo& GetOutputInfo(const struct wl_output* wl_output) { return output_info_map_[wl_output]; }
Expand Down Expand Up @@ -129,6 +131,8 @@ class WaylandContext : public WsiContext
static void HandleOutputDone(void* data, struct wl_output* wl_output);
static void HandleOutputScale(void* data, struct wl_output* wl_output, int32_t factor);

static void HandleXdgWmBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial);

typedef std::unordered_map<struct wl_surface*, WaylandWindow*> WaylandWindowMap;
typedef std::unordered_map<const struct wl_output*, OutputInfo> OutputInfoMap;

Expand All @@ -137,8 +141,10 @@ class WaylandContext : public WsiContext
static struct wl_seat_listener seat_listener_;
static struct wl_registry_listener registry_listener_;
static struct wl_output_listener output_listener_;
static struct xdg_wm_base_listener xdg_wm_base_listener_;
struct wl_display* display_{};
struct wl_shell* shell_{};
struct xdg_wm_base* xdg_wm_base_{};
struct wl_compositor* compositor_{};
struct wl_registry* registry_{};
struct wl_seat* seat_{};
Expand Down
144 changes: 120 additions & 24 deletions framework/application/wayland_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,41 @@ GFXRECON_BEGIN_NAMESPACE(application)

struct wl_surface_listener WaylandWindow::surface_listener_;
struct wl_shell_surface_listener WaylandWindow::shell_surface_listener_;
struct xdg_surface_listener WaylandWindow::xdg_surface_listener_;
struct xdg_toplevel_listener WaylandWindow::xdg_toplevel_listener_;

WaylandWindow::WaylandWindow(WaylandContext* wayland_context) :
wayland_context_(wayland_context), surface_(nullptr), shell_surface_(nullptr), width_(0), height_(0), scale_(1),
output_(nullptr)
wayland_context_(wayland_context), surface_(nullptr), shell_surface_(nullptr), xdg_surface_(nullptr),
xdg_toplevel_(nullptr), width_(0), height_(0), scale_(1), output_(nullptr), xdg_surface_configured_(false)
{
assert(wayland_context_ != nullptr);

// Populate callback structs
surface_listener_.enter = HandleSurfaceEnter;
surface_listener_.leave = HandleSurfaceLeave;

shell_surface_listener_.ping = HandlePing;
shell_surface_listener_.configure = HandleConfigure;
shell_surface_listener_.popup_done = HandlePopupDone;
shell_surface_listener_.ping = HandleShellSurfacePing;
shell_surface_listener_.configure = HandleShellSurfaceConfigure;
shell_surface_listener_.popup_done = HandleShellSurfacePopupDone;

xdg_surface_listener_.configure = HandleXdgSurfaceConfigure;

xdg_toplevel_listener_.configure = HandleXdgToplevelConfigure;
xdg_toplevel_listener_.close = HandleXdgToplevelClose;
}

WaylandWindow::~WaylandWindow()
{
auto& wl = wayland_context_->GetWaylandFunctionTable();
if (surface_)
if (surface_ != nullptr)
{
if (shell_surface_)
auto& wl = wayland_context_->GetWaylandFunctionTable();

if (xdg_toplevel_ != nullptr)
{
wl.xdg->xdg_toplevel_destroy(xdg_toplevel_);
wl.xdg->xdg_surface_destroy(xdg_surface_);
}
else if (shell_surface_ != nullptr)
{
wl.shell_surface_destroy(shell_surface_);
}
Expand All @@ -78,18 +91,59 @@ bool WaylandWindow::Create(
return false;
}

shell_surface_ = wl.shell_get_shell_surface(wayland_context_->GetShell(), surface_);
if (!shell_surface_)
// If we have the choice between xdg_toplevel and wl_shell_surface, chose the xdg_toplevel

if (wayland_context_->GetXdgWmBase() != nullptr)
{
GFXRECON_LOG_ERROR("Failed to create Wayland shell surface");
return false;
xdg_surface_ = wl.xdg->xdg_wm_base_get_xdg_surface(wayland_context_->GetXdgWmBase(), surface_);
if (xdg_surface_ != nullptr)
{
wl.xdg->xdg_surface_add_listener(xdg_surface_, &WaylandWindow::xdg_surface_listener_, this);
xdg_toplevel_ = wl.xdg->xdg_surface_get_toplevel(xdg_surface_);
}
}

if (xdg_toplevel_ == nullptr && wayland_context_->GetShell() != nullptr)
{
shell_surface_ = wl.shell_get_shell_surface(wayland_context_->GetShell(), surface_);
}

// Check what was created, setup listener for it, and clean up if nothing was successfuly created

wayland_context_->RegisterWaylandWindow(this);

if (xdg_toplevel_ != nullptr)
{
wl.xdg->xdg_toplevel_add_listener(xdg_toplevel_, &WaylandWindow::xdg_toplevel_listener_, this);
}
else if (shell_surface_ != nullptr)
{
wl.shell_surface_add_listener(shell_surface_, &WaylandWindow::shell_surface_listener_, this);
}
else
{
if (xdg_surface_ != nullptr)
{
wl.xdg->xdg_surface_destroy(xdg_surface_);
xdg_surface_ = nullptr;
}

GFXRECON_LOG_ERROR("Failed to create Wayland shell surface");
return false;
}

wl.surface_add_listener(surface_, &WaylandWindow::surface_listener_, this);
wl.shell_surface_add_listener(shell_surface_, &WaylandWindow::shell_surface_listener_, this);
wl.shell_surface_set_title(shell_surface_, title.c_str());

SetTitle(title);

if (xdg_toplevel_ != nullptr)
{
wl.surface_commit(surface_);
while (!xdg_surface_configured_)
{
wl.display_dispatch(wayland_context_->GetDisplay());
}
}

width_ = width;
height_ = height;
Expand All @@ -100,18 +154,30 @@ bool WaylandWindow::Create(

bool WaylandWindow::Destroy()
{
if (surface_)
if (surface_ != nullptr)
{
wayland_context_->UnregisterWaylandWindow(this);

auto& wl = wayland_context_->GetWaylandFunctionTable();
if (shell_surface_)

if (xdg_toplevel_ != nullptr)
{
wl.xdg->xdg_toplevel_destroy(xdg_toplevel_);
xdg_toplevel_ = nullptr;
wl.xdg->xdg_surface_destroy(xdg_surface_);
xdg_surface_ = nullptr;

xdg_surface_configured_ = false;
}
else if (shell_surface_ != nullptr)
{
wl.shell_surface_destroy(shell_surface_);
shell_surface_ = nullptr;
}

wl.surface_destroy(surface_);
wayland_context_->UnregisterWaylandWindow(this);
surface_ = nullptr;

return true;
}

Expand All @@ -121,7 +187,14 @@ bool WaylandWindow::Destroy()
void WaylandWindow::SetTitle(const std::string& title)
{
auto& wl = wayland_context_->GetWaylandFunctionTable();
wl.shell_surface_set_title(shell_surface_, title.c_str());
if (xdg_toplevel_ != nullptr)
{
wl.xdg->xdg_toplevel_set_title(xdg_toplevel_, title.c_str());
}
else if (shell_surface_ != nullptr)
{
wl.shell_surface_set_title(shell_surface_, title.c_str());
}
}

void WaylandWindow::SetPosition(const int32_t x, const int32_t y)
Expand Down Expand Up @@ -215,14 +288,21 @@ void WaylandWindow::UpdateWindowSize()

if (output_info.width == width_ && output_info.height == height_)
{
wl.shell_surface_set_fullscreen(shell_surface_, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, output_);
if (xdg_toplevel_ != nullptr)
{
wl.xdg->xdg_toplevel_set_fullscreen(xdg_toplevel_, output_);
}
else if (shell_surface_ != nullptr)
{
wl.shell_surface_set_fullscreen(shell_surface_, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, output_);
}
}
else
else if (shell_surface_ != nullptr)
{
wl.shell_surface_set_toplevel(shell_surface_);
}
}
else
else if (shell_surface_ != nullptr)
{
wl.shell_surface_set_toplevel(shell_surface_);
}
Expand All @@ -238,17 +318,33 @@ void WaylandWindow::HandleSurfaceEnter(void* data, struct wl_surface* surface, s

void WaylandWindow::HandleSurfaceLeave(void* data, struct wl_surface* surface, struct wl_output* output) {}

void WaylandWindow::HandlePing(void* data, wl_shell_surface* shell_surface, uint32_t serial)
void WaylandWindow::HandleShellSurfacePing(void* data, wl_shell_surface* shell_surface, uint32_t serial)
{
auto& wl = reinterpret_cast<WaylandWindow*>(data)->wayland_context_->GetWaylandFunctionTable();
wl.shell_surface_pong(shell_surface, serial);
}

void WaylandWindow::HandleConfigure(
void WaylandWindow::HandleShellSurfaceConfigure(
void* data, wl_shell_surface* shell_surface, uint32_t edges, int32_t width, int32_t height)
{}

void WaylandWindow::HandlePopupDone(void* data, wl_shell_surface* shell_surface) {}
void WaylandWindow::HandleShellSurfacePopupDone(void* data, wl_shell_surface* shell_surface) {}

void WaylandWindow::HandleXdgSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial)
{
WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);

auto& wl = window->wayland_context_->GetWaylandFunctionTable();

wl.xdg->xdg_surface_ack_configure(xdg_surface, serial);
window->xdg_surface_configured_ = true;
}

void WaylandWindow::HandleXdgToplevelConfigure(
void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, struct wl_array* states)
{}

void WaylandWindow::HandleXdgToplevelClose(void* data, struct xdg_toplevel* xdg_toplevel) {}

WaylandWindowFactory::WaylandWindowFactory(WaylandContext* wayland_context) : wayland_context_(wayland_context)
{
Expand Down
Loading
Loading