Bolderaysky
Bolderaysky

Reputation: 298

Determine size of the screen in Wayland C++

I have to write several functions which do some things with the user screen like gettings screen resolution, count of monitors etc. I have working solution for Windows and Linux, but on Linux I'm stuck with Wayland. On X11 size of the screen can be easily determined, but Wayland is relatively new and not so documented as X11.

I've tried the following code:

static void global_handler(void* data, wl_registry* registry, uint32_t name,
                           const char* interface, uint32_t version) {
    wl_output* output = static_cast<wl_output*>(
        wl_registry_bind(registry, name, &wl_output_interface, 1));
    if (!output) {
        // Handle error
    }
}

    std::string GetDisplaySize(std::string args) {

        if (getenv("WAYLAND_DISPLAY") != nullptr) {

            static std::uint16_t width, height;

            wl_display* display = wl_display_connect(nullptr);
            if (!display) { return "0"; }

            wl_registry* registry = wl_display_get_registry(display);

            static const wl_registry_listener registry_listener = {
                .global = +[](void* data, wl_registry* registry, uint32_t name,
                              const char* interface, uint32_t version) -> void {
                    wl_output* output = static_cast<wl_output*>(
                        wl_registry_bind(registry, name, &wl_output_interface,
                                         1));
                    if (!output) { return; }

                    wl_output_get_geometry(output, nullptr, nullptr, &width,
                                           &height);

                    wl_output_destroy(output);
                },
                .global_remove =
                    [](void* data, wl_registry* registry, uint32_t name) {
                    },
            };
            wl_registry_add_listener(registry, &registry_listener, nullptr);

            wl_display_roundtrip(display);

            wl_display_disconnect(display);

            return "{\"width\":" + std::to_string(width) +
                   ",\"height\":" + std::to_string(height) + "}";
        } // namespace api
        else {
            GdkRectangle workarea = {0};
            gdk_monitor_get_workarea(
                gdk_display_get_primary_monitor(gdk_display_get_default()),
                &workarea);

            return "{\"width\":" + std::to_string(workarea.width) +
                   ",\"height\":" + std::to_string(workarea.height) + "}";
        }
    }

This works for X11, but on Wayland I get compiler error

error: ‘wl_output_get_geometry’ was not declared in this scope

I'm using GCC C++ compiler. I tried to find some info about that wl_output_get_geometry or which function I can use instead, but I didn't find anything useful.

Upvotes: 3

Views: 1569

Answers (1)

Zenais
Zenais

Reputation: 146

I myself recently had the same issue, and I managed to find a solution using some code I found within the GLFW library, obviously adapted for my purposes. It's in C, but should still apply to C++ as well.

// Monitor.h

typedef struct wl_output monitor_information_t;
typedef struct wl_output_listener monitor_information_monitor_t;

typedef struct
{
    int32_t physical_width, physical_height;
    int32_t x, y;
    int32_t width, height;
    int16_t refresh_rate, scale;
} monitor_t;

extern monitor_t monitor_rendered_to;
extern monitor_information_t* monitor_information;
extern const monitor_information_monitor_t monitor_listener;
// Monitor.c

#include "Monitor.h"
#include <math.h>
#include <wayland-client-protocol.h>

monitor_t monitor_rendered_to;
monitor_information_t* monitor_information;

static void HandleMonitorGeometry(
    void* data, monitor_information_t* wl_output, int32_t x, int32_t y,
    int32_t physical_width, int32_t physical_height, int32_t subpixel,
    const char* make, const char* model, int32_t transform)
{
    monitor_rendered_to.x = x;
    monitor_rendered_to.y = y;
    monitor_rendered_to.physical_width = physical_width;
    monitor_rendered_to.physical_height = physical_height;
}

static void HandleMonitorPixelContents(void* data,
                                       monitor_information_t* wl_output,
                                       uint32_t flags, int32_t width,
                                       int32_t height, int32_t refresh)
{
    monitor_rendered_to.width = width;
    monitor_rendered_to.height = height;
    monitor_rendered_to.refresh_rate = (int16_t)round(refresh / 1000.0);
}

static void HandleMonitorInformationSent(void* data,
                                         monitor_information_t* wl_output)
{
    // No operation.
}

static void HandleMonitorScale(void* data,
                               monitor_information_t* wl_output,
                               int32_t factor)
{
    monitor_rendered_to.scale = factor;
}

static void HandleMonitorName(void* data, struct wl_output* wl_output,
                              const char* name)
{
    // No operation.
}

static void HandleMonitorDescription(void* data,
                                     struct wl_output* wl_output,
                                     const char* description)
{
    // No operation.
}

const monitor_information_monitor_t monitor_listener = {
    HandleMonitorGeometry,
    HandleMonitorPixelContents,
    HandleMonitorInformationSent,
    HandleMonitorScale,
    HandleMonitorName,
    HandleMonitorDescription};

And then within wherever you bind your registry objects;

static void HandleInterfaceAddition(void* data,
                                    struct wl_registry* registry,
                                    uint32_t name, const char* interface,
                                    uint32_t version)
{
    // [...]
    if (strcmp(interface, wl_output_interface.name) == 0)
    {
        monitor_information = wl_registry_bind(
            registry, name, &wl_output_interface, version);
        wl_output_add_listener(monitor_information, &monitor_listener,
                               NULL);
    }
    // [...]
}

static const interface_monitor_t registry_listener = {
    HandleInterfaceAddition};

I know this question is nearly 2 years old now, but I thought I should still post this answer just in case anyone has this problem in the future.

Upvotes: 1

Related Questions