312 lines
8.9 KiB
C
312 lines
8.9 KiB
C
|
// NOTE: Most of this code was stolen from wlr-randr, available at
|
||
|
// https://git.sr.ht/~emersion/wlr-randr/tree/master/item/main.c
|
||
|
|
||
|
#include "output_manager.h"
|
||
|
|
||
|
#include <wayland-client.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "protocol/wlr-output-management-unstable-v1.h"
|
||
|
|
||
|
static void head_handle_name(void* data,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
const char* name)
|
||
|
{
|
||
|
struct miix_wlr_head* head = data;
|
||
|
head->name = strdup(name);
|
||
|
}
|
||
|
|
||
|
static void head_handle_description(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
const char*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_physical_size(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
int32_t,
|
||
|
int32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_mode(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
struct zwlr_output_mode_v1*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_enabled(void*, struct zwlr_output_head_v1*, int32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_current_mode(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
struct zwlr_output_mode_v1*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_position(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
int32_t,
|
||
|
int32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_transform(void*, struct zwlr_output_head_v1*, int32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_scale(void*, struct zwlr_output_head_v1*, wl_fixed_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_finished(void*, struct zwlr_output_head_v1*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_make(void*, struct zwlr_output_head_v1*, const char*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_model(void*, struct zwlr_output_head_v1*, const char*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_serial_number(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
const char*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void head_handle_adaptive_sync(void*,
|
||
|
struct zwlr_output_head_v1*,
|
||
|
uint32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static const struct zwlr_output_head_v1_listener head_listener = {
|
||
|
.name = head_handle_name,
|
||
|
.description = head_handle_description,
|
||
|
.physical_size = head_handle_physical_size,
|
||
|
.mode = head_handle_mode,
|
||
|
.enabled = head_handle_enabled,
|
||
|
.current_mode = head_handle_current_mode,
|
||
|
.position = head_handle_position,
|
||
|
.transform = head_handle_transform,
|
||
|
.scale = head_handle_scale,
|
||
|
.finished = head_handle_finished,
|
||
|
.make = head_handle_make,
|
||
|
.model = head_handle_model,
|
||
|
.serial_number = head_handle_serial_number,
|
||
|
.adaptive_sync = head_handle_adaptive_sync,
|
||
|
};
|
||
|
|
||
|
static void output_manager_handle_head(void* data,
|
||
|
struct zwlr_output_manager_v1*,
|
||
|
struct zwlr_output_head_v1* wlr_head)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
|
||
|
struct miix_wlr_head* head = calloc(1, sizeof(*head));
|
||
|
head->wlr_head = wlr_head;
|
||
|
wl_list_insert(state->heads.prev, &head->link);
|
||
|
zwlr_output_head_v1_add_listener(wlr_head, &head_listener, head);
|
||
|
}
|
||
|
|
||
|
static void output_manager_handle_done(void* data,
|
||
|
struct zwlr_output_manager_v1*,
|
||
|
uint32_t serial)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
state->serial = serial;
|
||
|
}
|
||
|
|
||
|
static void output_manager_handle_finished(void*,
|
||
|
struct zwlr_output_manager_v1*)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static const struct zwlr_output_manager_v1_listener output_manager_listener = {
|
||
|
.head = output_manager_handle_head,
|
||
|
.done = output_manager_handle_done,
|
||
|
.finished = output_manager_handle_finished,
|
||
|
};
|
||
|
|
||
|
static void registry_handle_global(void* data,
|
||
|
struct wl_registry* registry,
|
||
|
uint32_t name,
|
||
|
const char* interface,
|
||
|
uint32_t)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
|
||
|
if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) {
|
||
|
// TODO: Don't always bind version 4?
|
||
|
state->output_manager = wl_registry_bind(
|
||
|
registry, name, &zwlr_output_manager_v1_interface, 4);
|
||
|
zwlr_output_manager_v1_add_listener(
|
||
|
state->output_manager, &output_manager_listener, state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void registry_handle_global_remove(void*, struct wl_registry*, uint32_t)
|
||
|
{
|
||
|
// NO-OP
|
||
|
}
|
||
|
|
||
|
static void config_handle_succeeded(void* data,
|
||
|
struct zwlr_output_configuration_v1* config)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
state->is_working = false;
|
||
|
zwlr_output_configuration_v1_destroy(config);
|
||
|
printf("Applied configuration successfully!\n");
|
||
|
}
|
||
|
|
||
|
static void config_handle_failed(void* data,
|
||
|
struct zwlr_output_configuration_v1* config)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
state->is_working = false;
|
||
|
zwlr_output_configuration_v1_destroy(config);
|
||
|
fprintf(stderr, "Failed to apply configuration\n");
|
||
|
}
|
||
|
|
||
|
static void config_handle_cancelled(void* data,
|
||
|
struct zwlr_output_configuration_v1* config)
|
||
|
{
|
||
|
struct miix_wlr_state* state = data;
|
||
|
state->is_working = false;
|
||
|
zwlr_output_configuration_v1_destroy(config);
|
||
|
fprintf(stderr, "Configuration cancelled\n");
|
||
|
}
|
||
|
|
||
|
static const struct zwlr_output_configuration_v1_listener config_listener = {
|
||
|
.succeeded = config_handle_succeeded,
|
||
|
.failed = config_handle_failed,
|
||
|
.cancelled = config_handle_cancelled,
|
||
|
};
|
||
|
|
||
|
struct miix_wlr_state* miix_wlr_init()
|
||
|
{
|
||
|
struct wl_display* display = wl_display_connect(NULL);
|
||
|
if (!display) {
|
||
|
fprintf(stderr, "Couldn't connect to display\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct wl_registry* registry = wl_display_get_registry(display);
|
||
|
if (!registry) {
|
||
|
fprintf(stderr, "Couldn't get display registry\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct miix_wlr_state* state = calloc(1, sizeof(struct miix_wlr_state));
|
||
|
|
||
|
state->is_working = false;
|
||
|
state->display = display;
|
||
|
|
||
|
wl_list_init(&state->heads);
|
||
|
|
||
|
static const struct wl_registry_listener registry_listener = {
|
||
|
.global = registry_handle_global,
|
||
|
.global_remove = registry_handle_global_remove,
|
||
|
};
|
||
|
wl_registry_add_listener(registry, ®istry_listener, state);
|
||
|
|
||
|
wl_display_dispatch(display);
|
||
|
wl_display_roundtrip(display);
|
||
|
wl_registry_destroy(registry);
|
||
|
|
||
|
if (!state->output_manager) {
|
||
|
fprintf(stderr,
|
||
|
"Couldn't get output manager. Does your compositor support "
|
||
|
"wlr-outpput-management-unstable-v1?\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
char** miix_wlr_get_head_names(struct miix_wlr_state const* state)
|
||
|
{
|
||
|
int head_count = wl_list_length(&state->heads);
|
||
|
char** head_name_list = calloc(head_count + 1, sizeof(char*));
|
||
|
size_t head_index = 0;
|
||
|
|
||
|
struct miix_wlr_head *head, *tmp_head;
|
||
|
wl_list_for_each_safe(head, tmp_head, &state->heads, link)
|
||
|
{
|
||
|
head_name_list[head_index] = head->name;
|
||
|
++head_index;
|
||
|
}
|
||
|
|
||
|
head_name_list[head_index] = 0;
|
||
|
return head_name_list;
|
||
|
}
|
||
|
|
||
|
void miix_wlr_head_set_transform(struct miix_wlr_state* state,
|
||
|
char const* const head_name,
|
||
|
enum wl_output_transform transform)
|
||
|
{
|
||
|
struct zwlr_output_configuration_v1* output_config =
|
||
|
zwlr_output_manager_v1_create_configuration(state->output_manager,
|
||
|
state->serial);
|
||
|
|
||
|
zwlr_output_configuration_v1_add_listener(
|
||
|
output_config, &config_listener, state);
|
||
|
|
||
|
struct miix_wlr_head* head;
|
||
|
struct miix_wlr_head* tmp_head;
|
||
|
wl_list_for_each_safe(head, tmp_head, &state->heads, link)
|
||
|
{
|
||
|
if (strcmp(head->name, head_name) != 0) continue;
|
||
|
|
||
|
printf("Found head %s\n", head->name);
|
||
|
|
||
|
struct zwlr_output_configuration_head_v1* config_head =
|
||
|
zwlr_output_configuration_v1_enable_head(output_config,
|
||
|
head->wlr_head);
|
||
|
|
||
|
state->is_working = true;
|
||
|
|
||
|
zwlr_output_configuration_head_v1_set_transform(config_head, transform);
|
||
|
zwlr_output_configuration_v1_apply(output_config);
|
||
|
}
|
||
|
|
||
|
while (state->is_working && wl_display_dispatch(state->display) > 0)
|
||
|
;
|
||
|
}
|
||
|
|
||
|
void miix_wlr_cleanup(struct miix_wlr_state* state)
|
||
|
{
|
||
|
struct miix_wlr_head* head;
|
||
|
struct miix_wlr_head* tmp_head;
|
||
|
wl_list_for_each_safe(head, tmp_head, &state->heads, link)
|
||
|
{
|
||
|
zwlr_output_head_v1_destroy(head->wlr_head);
|
||
|
free(head->name);
|
||
|
free(head);
|
||
|
}
|
||
|
|
||
|
zwlr_output_manager_v1_destroy(state->output_manager);
|
||
|
wl_display_disconnect(state->display);
|
||
|
}
|