Skip to content

Commit

Permalink
Support for XDG Shell on Wayland (#1190)
Browse files Browse the repository at this point in the history
* Support for XDG Shell on Wayland

Add support for XDG Shell on Wayland in addition to the classic Wayland
shell and make XDG Shell the default choice when both are available.

Change-Id: Ib5521c699275e23060c4e7ae05696ed8fc3f7221

* Add Wayland protocol dynamic loader generator

Add Python script `generate_wayland.py` that generates source
code from a Wayland XML protocol. This script has the same
role as the `wayland-scanner` tool, except that the sources
generated use the GFXReconstruct `WaylandLoader` instead of
directly using `libwayland-client.so` symbols.

The current state now is:
- If the machine that compiles the code does not support
Wayland (no `libwayland-client.so`) then the Wayland code
is simply not compiled and the executable will not support
Wayland (as done until now)
- If the machine that compiles the code supports Wayland
and the executable is ran on a machine that supports
Wayland, then the `libwayland-client.so` of the client
will be loaded using `dlopen` and the generated protocol
sources will initialize protocol constants (interfaces).
- If the machine that compiles the code supports Wayland
but the executable is ran on a machine that does not
support Wayland, then the WaylandLoader will remain
uninitialized and thus not call the generated protocol
sources and not attempt to initialize the protocol
constants.

This way, the binaries will be able no matter the state of
the support of Wayland on the machine that runs them.

Change-Id: I9b3878a71b74002859f4edda6d9a6fe180278a8f

* Remove wayland-protocols as a submodule

Changes requested by LunarG: Remove wayland-protocols submodule
and download it from generate_wayland.py instead.

Change-Id: I29932c82d0c4b847f94e47dd191e6fcb4406157a
  • Loading branch information
marius-pelegrin-arm authored Mar 8, 2024
1 parent b9a9916 commit 75dc7dd
Show file tree
Hide file tree
Showing 10 changed files with 1,124 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ __pycache__/
*.rdbg
._*
.DS_Store
external/wayland-protocols/
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
21 changes: 16 additions & 5 deletions framework/application/wayland_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class WaylandWindow : public decode::Window

struct wl_shell_surface* GetShellSurface() const { return shell_surface_; }

struct xdg_toplevel* GetXdgToplevel() const { return xdg_toplevel_; }

virtual bool Create(const std::string& title,
const int32_t x,
const int32_t y,
Expand Down Expand Up @@ -80,25 +82,34 @@ class WaylandWindow : public decode::Window
static void HandleSurfaceEnter(void* data, struct wl_surface* surface, struct wl_output* output);
static void HandleSurfaceLeave(void* data, struct wl_surface* surface, struct wl_output* output);

static void HandlePing(void* data, wl_shell_surface* shell_surface, uint32_t serial);
static void HandleShellSurfacePing(void* data, wl_shell_surface* shell_surface, uint32_t serial);
static void HandleShellSurfaceConfigure(
void* data, wl_shell_surface* shell_surface, uint32_t edges, int32_t width, int32_t height);
static void HandleShellSurfacePopupDone(void* data, wl_shell_surface* shell_surface);

static void
HandleConfigure(void* data, wl_shell_surface* shell_surface, uint32_t edges, int32_t width, int32_t height);
static void HandleXdgSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial);

static void HandlePopupDone(void* data, wl_shell_surface* shell_surface);
static void HandleXdgToplevelConfigure(
void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, struct wl_array* states);
static void HandleXdgToplevelClose(void* data, struct xdg_toplevel* xdg_toplevel);

void UpdateWindowSize();

private:
static struct wl_surface_listener surface_listener_;
static struct wl_shell_surface_listener shell_surface_listener_;
static struct xdg_surface_listener xdg_surface_listener_;
static struct xdg_toplevel_listener xdg_toplevel_listener_;
WaylandContext* wayland_context_;
struct wl_surface* surface_;
struct wl_shell_surface* shell_surface_;
struct xdg_surface* xdg_surface_;
struct xdg_toplevel* xdg_toplevel_;
uint32_t width_;
uint32_t height_;
int32_t scale_;
struct wl_output* output_;
bool xdg_surface_configured_;
};

class WaylandWindowFactory : public decode::WindowFactory
Expand All @@ -124,4 +135,4 @@ class WaylandWindowFactory : public decode::WindowFactory
GFXRECON_END_NAMESPACE(util)
GFXRECON_END_NAMESPACE(gfxrecon)

#endif // GFXRECON_APPLICATION_WAYLAND_WINDOW_H
#endif // GFXRECON_APPLICATION_WAYLAND_WINDOW_H
Loading

0 comments on commit 75dc7dd

Please sign in to comment.