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 <cstddef>
#include <cstdio>
#include <cstring>
#include <dlfcn.h>
#include <memory>
#include <string>
#include <vector>

#include <wayland-client.h>

#include "outputs.h"
#include "unity.h"

static std::vector<const char*> quality_levels = {"Low", "High"};
static int latest_display = 0;
static void *handle = nullptr;

PFN_SetupUnityCallbacks SetupUnityCallbacks = nullptr;
PFN_LoadScreenSelectorWindow LoadScreenSelectorWindow = nullptr;

int GetAxisDescription(int axis, char *control, char *primary, char *secondary)
{
	printf("GetAxisDescription(%d)\n", axis);
	switch (axis) {
	case 0:
		strncpy(control, "Horizontal (+)", 8192);
		strncpy(primary, "right", 8192);
		strncpy(secondary, "d", 8192);
		break;
	case 1:
		strncpy(control, "Horizontal (-)", 8192);
		strncpy(primary, "left", 8192);
		strncpy(secondary, "a", 8192);
		break;
	case 2:
		strncpy(control, "Vertical (+)", 8192);
		strncpy(primary, "up", 8192);
		strncpy(secondary, "w", 8192);
		break;
	case 3:
		strncpy(control, "Vertical (-)", 8192);
		strncpy(primary, "down", 8192);
		strncpy(secondary, "s", 8192);
		break;
	default:
		control[0] = '\0';
		primary[0] = '\0';
		secondary[0] = '\0';
		return 1;
	}
	return 0;
}

char ConfigureAxis(int axis, int enabled)
{
	printf("ConfigureAxis(%d, %d) -> %d\n", axis, enabled, 1);
	return 1;
}

std::vector<struct resolution>* GetResolutions(int display)
{
	printf("GetResolutions(display=%d)\n", display);

	// This is required so that GetSelectedResolution can give the correct result…
	latest_display = display;

	return &outputs[display]->resolutions;
}

void GetSelectedResolution(int *width, int *height, bool *windowed)
{
	const struct output_info *info = outputs[latest_display].get();
	*width = info->resolutions[info->default_resolution].width;
	*height = info->resolutions[info->default_resolution].height;
	*windowed = false;

	printf("GetSelectedResolution() -> (%d×%d, windowed=%d)\n", *width, *height, *windowed);
}

void SetSelectedResolution(int width, int height, bool windowed)
{
	printf("SetSelectedResolution(width=%d, height=%d, windowed=%d)\n", width, height, windowed);
}

std::vector<const char *>* GetQualityLevels()
{
	printf("GetQualityLevels()\n");
	return &quality_levels;
}

int GetSelectedQualityLevel()
{
	printf("GetSelectedQualityLevel() -> %d\n", 1);
	return 1;
}

void SetSelectedQualityLevel(int quality)
{
	printf("SetSelectedQualityLevel(quality=%d)\n", quality);
}

std::vector<const char *>* GetDisplays()
{
	static std::vector<const char*> displays;
	displays.clear();
	for (const auto& output : outputs)
		displays.push_back(output->model.c_str());

	printf("GetDisplays()\n");

	return &displays;
}

int GetSelectedDisplay()
{
	int ret = 0;
	printf("GetSelectedDisplay() -> %d\n", ret);
	return ret;
}

void SetSelectedDisplay(int display)
{
	printf("SetSelectedDisplay(display=%d)\n", display);
}

int load_screen_selector()
{
	handle = dlopen("./ScreenSelector.so", RTLD_NOW);
	if (!handle) {
		fprintf(stderr, "%s\n", dlerror());
		return -1;
	}

	SetupUnityCallbacks = reinterpret_cast<PFN_SetupUnityCallbacks>(dlsym(handle, "SetupUnityCallbacks"));
	if (!SetupUnityCallbacks) {
		fprintf(stderr, "%s\n", dlerror());
		return -1;
	}

	LoadScreenSelectorWindow = reinterpret_cast<PFN_LoadScreenSelectorWindow>(dlsym(handle, "LoadScreenSelectorWindow"));
	if (!LoadScreenSelectorWindow) {
		fprintf(stderr, "%s\n", dlerror());
		return -1;
	}

	return 0;
}

int unload_screen_selector()
{
	int ret = dlclose(handle);
	if (ret) {
		fprintf(stderr, "Failed to close ScreenSelector.so: %s\n", dlerror());
		return -1;
	}

	return 0;
}