screen-selector 0.1.1

A reimplementation of Unity’s ScreenSelector.so plugin, using GTK 4
Documentation
// SPDX-License-Identifier: GPL-3.0-only

#include <cerrno>
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>

#include <wayland-client.h>

#include "outputs.h"

// The list of outputs and their characteritics, to be used by the other translation units.
std::vector<std::unique_ptr<struct output_info>> outputs = {};

static void handle_geometry(void *data, struct wl_output *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)
{
	(void)wl_output;
	(void)x;
	(void)y;
	(void)physical_width;
	(void)physical_height;
	(void)subpixel;
	(void)transform;

	struct output_info *output = static_cast<struct output_info *>(data);
	output->make = make;
	output->model = model;
}

static void handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh)
{
	(void)wl_output;
	(void)refresh;

	struct output_info *output = static_cast<struct output_info *>(data);

	if (flags & WL_OUTPUT_MODE_CURRENT)
		output->default_resolution = output->resolutions.size();

	output->resolutions.push_back({width, height});
}

static void handle_done(void *data, struct wl_output *wl_output)
{
	(void)data;
	(void)wl_output;
}

static void handle_scale(void *data, struct wl_output *wl_output, int32_t factor)
{
	(void)data;
	(void)wl_output;
	(void)factor;
}

static struct wl_output_listener output_listener = {
	.geometry = handle_geometry,
	.mode = handle_mode,
	.done = handle_done,
	.scale = handle_scale,
};

static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
{
	(void)data;

	if (strcmp(interface, wl_output_interface.name) == 0) {
		std::unique_ptr<struct output_info> output = std::make_unique<struct output_info>();
		int output_version = std::min(version, 1U);
		output->global = static_cast<struct wl_output *>(wl_registry_bind(registry, name, &wl_output_interface, output_version));
		wl_output_add_listener(output->global, &output_listener, output.get());
		outputs.push_back(std::move(output));
	}
}

static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
	(void)data;
	(void)registry;
	(void)name;

	// We don’t need to do anything here, we’ll destroy everything right away anyway.
}

static const struct wl_registry_listener registry_listener = {
	.global = handle_global,
	.global_remove = handle_global_remove,
};

int populate_wayland_outputs()
{
	// Initialise the Wayland connection and registry.
	struct wl_display *display = wl_display_connect(NULL);
	if (!display) {
		fprintf(stderr, "Wayland: Failed to connect to display: %s\n", strerror(errno));
		return -1;
	}
	
	struct wl_registry *registry = wl_display_get_registry(display);
	if (!registry) {
		fprintf(stderr, "Wayland: Failed to obtain a registry.\n");
		return -1;
	}

	wl_registry_add_listener(registry, &registry_listener, NULL);

	// Obtain the wl_output globals.
	wl_display_roundtrip(display);

	// Receive these outputs’ events, and handle them.
	wl_display_roundtrip(display);

	// We can now close the Wayland connection, we got everything we needed!
	for (const auto& output : outputs)
		wl_output_destroy(output->global);
	wl_registry_destroy(registry);
	wl_display_disconnect(display);

	return 0;
}