initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6a6a6964ee9e6ebe436ca8328c6e4a7ec7c9d8d4
This commit is contained in:
commit
fcc080871a
11 changed files with 12536 additions and 0 deletions
402
src/wayland.c
Normal file
402
src/wayland.c
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/chroma.h"
|
||||
|
||||
// Registry listener
|
||||
static void registry_global(void *data, struct wl_registry *registry,
|
||||
uint32_t id, const char *interface,
|
||||
uint32_t version) {
|
||||
chroma_state_t *state = (chroma_state_t *)data;
|
||||
|
||||
chroma_log("DEBUG", "Registry global: %s (id=%u, version=%u)", interface, id,
|
||||
version);
|
||||
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
state->compositor = wl_registry_bind(registry, id, &wl_compositor_interface,
|
||||
version < 4 ? version : 4);
|
||||
if (!state->compositor) {
|
||||
chroma_log("ERROR", "Failed to bind compositor");
|
||||
} else {
|
||||
chroma_log("INFO", "Bound compositor (version %u)", version);
|
||||
}
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
state->layer_shell =
|
||||
wl_registry_bind(registry, id, &zwlr_layer_shell_v1_interface,
|
||||
version < 4 ? version : 4);
|
||||
if (!state->layer_shell) {
|
||||
chroma_log("ERROR", "Failed to bind layer shell");
|
||||
} else {
|
||||
chroma_log("INFO", "Bound layer shell (version %u)", version);
|
||||
}
|
||||
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct wl_output *output = wl_registry_bind(
|
||||
registry, id, &wl_output_interface, version < 4 ? version : 4);
|
||||
if (!output) {
|
||||
chroma_log("ERROR", "Failed to bind output %u", id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chroma_output_add(state, id, output) != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to add output %u", id);
|
||||
wl_output_destroy(output);
|
||||
} else {
|
||||
chroma_log("INFO", "Added output %u", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void registry_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t id) {
|
||||
chroma_state_t *state = (chroma_state_t *)data;
|
||||
(void)registry; // Unused parameter
|
||||
|
||||
chroma_log("DEBUG", "Registry global remove: id=%u", id);
|
||||
chroma_output_remove(state, id);
|
||||
}
|
||||
|
||||
const struct wl_registry_listener chroma_registry_listener_impl = {
|
||||
.global = registry_global,
|
||||
.global_remove = registry_global_remove,
|
||||
};
|
||||
|
||||
/* Layer surface event handlers */
|
||||
static void layer_surface_configure(void *data,
|
||||
struct zwlr_layer_surface_v1 *layer_surface,
|
||||
uint32_t serial, uint32_t width,
|
||||
uint32_t height) {
|
||||
chroma_output_t *output = (chroma_output_t *)data;
|
||||
(void)layer_surface; // Unused parameter
|
||||
|
||||
chroma_log("DEBUG", "Layer surface configure: %ux%u, serial=%u", width,
|
||||
height, serial);
|
||||
|
||||
output->configure_serial = serial;
|
||||
|
||||
/* Acknowledge the configure event */
|
||||
zwlr_layer_surface_v1_ack_configure(output->layer_surface, serial);
|
||||
|
||||
/* Commit the surface to apply the acknowledgment */
|
||||
wl_surface_commit(output->surface);
|
||||
|
||||
chroma_log("DEBUG", "Acknowledged layer surface configure for output %u",
|
||||
output->id);
|
||||
}
|
||||
|
||||
static void layer_surface_closed(void *data,
|
||||
struct zwlr_layer_surface_v1 *layer_surface) {
|
||||
chroma_output_t *output = (chroma_output_t *)data;
|
||||
(void)layer_surface; /* Unused parameter */
|
||||
|
||||
chroma_log("INFO", "Layer surface closed for output %u", output->id);
|
||||
|
||||
/* Clean up the surface */
|
||||
if (output->surface) {
|
||||
chroma_surface_destroy(output);
|
||||
}
|
||||
}
|
||||
|
||||
const struct zwlr_layer_surface_v1_listener chroma_layer_surface_listener_impl =
|
||||
{
|
||||
.configure = layer_surface_configure,
|
||||
.closed = layer_surface_closed,
|
||||
};
|
||||
|
||||
/* Output event handlers */
|
||||
static void output_geometry(void *data, struct wl_output *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) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; // Unused parameter
|
||||
(void)subpixel; // Unused parameter
|
||||
(void)make; // Unused parameter
|
||||
(void)model; // Unused parameter
|
||||
|
||||
chroma_output->x = x;
|
||||
chroma_output->y = y;
|
||||
chroma_output->transform = transform;
|
||||
|
||||
chroma_log("DEBUG", "Output %u geometry: %dx%d at (%d,%d), transform=%d",
|
||||
chroma_output->id, physical_width, physical_height, x, y,
|
||||
transform);
|
||||
}
|
||||
|
||||
static void output_mode(void *data, struct wl_output *output, uint32_t flags,
|
||||
int32_t width, int32_t height, int32_t refresh) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; // Unused parameter
|
||||
|
||||
if (flags & WL_OUTPUT_MODE_CURRENT) {
|
||||
chroma_output->width = width;
|
||||
chroma_output->height = height;
|
||||
chroma_log("DEBUG", "Output %u mode: %dx%d@%d (current)", chroma_output->id,
|
||||
width, height, refresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void output_scale(void *data, struct wl_output *output, int32_t scale) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; // Unused parameter
|
||||
|
||||
chroma_output->scale = scale;
|
||||
chroma_log("DEBUG", "Output %u scale: %d", chroma_output->id, scale);
|
||||
}
|
||||
|
||||
static void output_name(void *data, struct wl_output *output,
|
||||
const char *name) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; // Unused parameter
|
||||
|
||||
free(chroma_output->name);
|
||||
chroma_output->name = strdup(name);
|
||||
if (!chroma_output->name) {
|
||||
chroma_log("ERROR", "Failed to allocate memory for output name");
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_log("DEBUG", "Output %u name: %s", chroma_output->id, name);
|
||||
}
|
||||
|
||||
static void output_description(void *data, struct wl_output *output,
|
||||
const char *description) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; // Unused parameter
|
||||
|
||||
free(chroma_output->description);
|
||||
chroma_output->description = strdup(description);
|
||||
if (!chroma_output->description) {
|
||||
chroma_log("ERROR", "Failed to allocate memory for output description");
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_log("DEBUG", "Output %u description: %s", chroma_output->id,
|
||||
description);
|
||||
}
|
||||
|
||||
static void output_done(void *data, struct wl_output *output) {
|
||||
chroma_output_t *chroma_output = (chroma_output_t *)data;
|
||||
(void)output; /* Unused parameter */
|
||||
|
||||
chroma_log("DEBUG", "Output %u done - configuration complete",
|
||||
chroma_output->id);
|
||||
|
||||
// Mark output as active and ready for wallpaper assignment
|
||||
chroma_output->active = true;
|
||||
|
||||
// Trigger wallpaper assignment for this output
|
||||
if (chroma_output->state) {
|
||||
handle_output_done(chroma_output->state, chroma_output);
|
||||
}
|
||||
}
|
||||
|
||||
const struct wl_output_listener chroma_output_listener_impl = {
|
||||
.geometry = output_geometry,
|
||||
.mode = output_mode,
|
||||
.scale = output_scale,
|
||||
.name = output_name,
|
||||
.description = output_description,
|
||||
.done = output_done,
|
||||
};
|
||||
|
||||
/* Wayland connection functions */
|
||||
int chroma_wayland_connect(chroma_state_t *state) {
|
||||
if (!state) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
// Connect to Wayland display
|
||||
state->display = wl_display_connect(NULL);
|
||||
if (!state->display) {
|
||||
chroma_log("ERROR", "Failed to connect to Wayland display: %s",
|
||||
strerror(errno));
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
// Get registry
|
||||
state->registry = wl_display_get_registry(state->display);
|
||||
if (!state->registry) {
|
||||
chroma_log("ERROR", "Failed to get Wayland registry");
|
||||
chroma_wayland_disconnect(state);
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
// Add registry listener
|
||||
wl_registry_add_listener(state->registry, &chroma_registry_listener_impl,
|
||||
state);
|
||||
|
||||
// Roundtrip to get all globals
|
||||
if (wl_display_roundtrip(state->display) == -1) {
|
||||
chroma_log("ERROR", "Failed to roundtrip Wayland display");
|
||||
chroma_wayland_disconnect(state);
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
// Check if we got a compositor
|
||||
if (!state->compositor) {
|
||||
chroma_log("ERROR", "No compositor available");
|
||||
chroma_wayland_disconnect(state);
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
// Check if we got layer shell
|
||||
if (!state->layer_shell) {
|
||||
chroma_log("ERROR", "No layer shell available - compositor may not support "
|
||||
"wlr-layer-shell");
|
||||
chroma_wayland_disconnect(state);
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Connected to Wayland display, found %d outputs",
|
||||
state->output_count);
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
void chroma_wayland_disconnect(chroma_state_t *state) {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up all outputs
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
chroma_output_t *output = &state->outputs[i];
|
||||
if (output->surface) {
|
||||
chroma_surface_destroy(output);
|
||||
}
|
||||
|
||||
if (output->wl_output) {
|
||||
wl_output_destroy(output->wl_output);
|
||||
}
|
||||
|
||||
free(output->name);
|
||||
free(output->description);
|
||||
}
|
||||
state->output_count = 0;
|
||||
|
||||
// Clean up Wayland objects
|
||||
if (state->layer_shell) {
|
||||
zwlr_layer_shell_v1_destroy(state->layer_shell);
|
||||
state->layer_shell = NULL;
|
||||
}
|
||||
|
||||
if (state->compositor) {
|
||||
wl_compositor_destroy(state->compositor);
|
||||
state->compositor = NULL;
|
||||
}
|
||||
|
||||
if (state->registry) {
|
||||
wl_registry_destroy(state->registry);
|
||||
state->registry = NULL;
|
||||
}
|
||||
|
||||
if (state->display) {
|
||||
wl_display_disconnect(state->display);
|
||||
state->display = NULL;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Disconnected from Wayland display");
|
||||
}
|
||||
|
||||
/* Output management functions */
|
||||
int chroma_output_add(chroma_state_t *state, uint32_t id,
|
||||
struct wl_output *output) {
|
||||
if (!state || !output) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
if (state->output_count >= MAX_OUTPUTS) {
|
||||
chroma_log("ERROR", "Maximum number of outputs (%d) exceeded", MAX_OUTPUTS);
|
||||
return CHROMA_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
chroma_output_t *chroma_output = &state->outputs[state->output_count];
|
||||
memset(chroma_output, 0, sizeof(chroma_output_t));
|
||||
|
||||
chroma_output->wl_output = output;
|
||||
chroma_output->id = id;
|
||||
chroma_output->scale = 1; // Default scale
|
||||
chroma_output->active = false;
|
||||
chroma_output->state = state;
|
||||
|
||||
// Add output listener
|
||||
wl_output_add_listener(output, &chroma_output_listener_impl, chroma_output);
|
||||
|
||||
state->output_count++;
|
||||
|
||||
chroma_log("INFO", "Added output %u (total: %d)", id, state->output_count);
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
void chroma_output_remove(chroma_state_t *state, uint32_t id) {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_output_t *output = chroma_output_find_by_id(state, id);
|
||||
if (!output) {
|
||||
chroma_log("WARN", "Attempted to remove non-existent output %u", id);
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Removing output %u (%s)", id,
|
||||
output->name ? output->name : "unknown");
|
||||
|
||||
/* Clean up surface if it exists */
|
||||
if (output->surface) {
|
||||
chroma_surface_destroy(output);
|
||||
}
|
||||
|
||||
/* Clean up Wayland output */
|
||||
if (output->wl_output) {
|
||||
wl_output_destroy(output->wl_output);
|
||||
}
|
||||
|
||||
/* Free allocated strings */
|
||||
free(output->name);
|
||||
free(output->description);
|
||||
|
||||
/* Remove from array by shifting remaining elements */
|
||||
int index = output - state->outputs;
|
||||
int remaining = state->output_count - index - 1;
|
||||
if (remaining > 0) {
|
||||
memmove(output, output + 1, remaining * sizeof(chroma_output_t));
|
||||
}
|
||||
|
||||
state->output_count--;
|
||||
chroma_log("INFO", "Removed output %u (remaining: %d)", id,
|
||||
state->output_count);
|
||||
}
|
||||
|
||||
chroma_output_t *chroma_output_find_by_id(chroma_state_t *state, uint32_t id) {
|
||||
if (!state) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
if (state->outputs[i].id == id) {
|
||||
return &state->outputs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chroma_output_t *chroma_output_find_by_name(chroma_state_t *state,
|
||||
const char *name) {
|
||||
if (!state || !name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
chroma_output_t *output = &state->outputs[i];
|
||||
if (output->name && strcmp(output->name, name) == 0) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue