#include #include #include #include #include #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); chroma_log("TRACE", "Checking interface binding for: %s", interface); 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); chroma_log("TRACE", "Compositor binding successful, interface ready"); } } 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); chroma_log( "TRACE", "wlr-layer-shell protocol available, wallpaper support enabled"); } } 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; chroma_log("DEBUG", "Registry global remove: id=%u", id); chroma_log("TRACE", "Global object removal initiated for 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; chroma_log("DEBUG", "Layer surface configure: %ux%u, serial=%u", width, height, serial); chroma_log( "TRACE", "Configuring layer surface for output %u: dimensions=%ux%u, serial=%u", output->id, width, height, serial); output->configure_serial = serial; // Acknowledge the configure event zwlr_layer_surface_v1_ack_configure(output->layer_surface, serial); chroma_log("TRACE", "Sent configure acknowledgment for output %u serial %u", output->id, serial); // Commit the surface to apply the acknowledgment wl_surface_commit(output->surface); chroma_log("TRACE", "Surface committed for output %u", output->id); 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; 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; (void)subpixel; (void)make; (void)model; 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); chroma_log("TRACE", "Output %u geometry details: physical_size=%dx%dmm, " "position=(%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; 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); chroma_log("TRACE", "Current mode set for output %u: %dx%d@%dHz, flags=0x%x", chroma_output->id, width, height, refresh, flags); } else { chroma_log("TRACE", "Non-current mode for output %u: %dx%d@%dHz, flags=0x%x", chroma_output->id, width, height, refresh, flags); } } static void output_scale(void *data, struct wl_output *output, int32_t scale) { chroma_output_t *chroma_output = (chroma_output_t *)data; (void)output; 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; 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; 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; chroma_log("DEBUG", "Output %u done - configuration complete", chroma_output->id); chroma_log("TRACE", "Output %u configuration finalized: %dx%d, scale=%d, name='%s'", chroma_output->id, chroma_output->width, chroma_output->height, chroma_output->scale, chroma_output->name ? chroma_output->name : "unknown"); // Mark output as active and ready for wallpaper assignment chroma_output->active = true; // Trigger wallpaper assignment for this output if (chroma_output->state) { chroma_log("TRACE", "Triggering wallpaper assignment for output %u", chroma_output->id); 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 chroma_log("TRACE", "Starting Wayland display roundtrip to discover globals"); if (wl_display_roundtrip(state->display) == -1) { chroma_log("ERROR", "Failed to roundtrip Wayland display"); chroma_wayland_disconnect(state); return CHROMA_ERROR_WAYLAND; } chroma_log("TRACE", "Wayland display roundtrip completed"); // 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); chroma_log("TRACE", "Output listener attached for output %u", id); state->output_count++; chroma_log("INFO", "Added output %u (total: %d)", id, state->output_count); chroma_log("TRACE", "Output %u initialized with default scale=1, active=false", id); 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 ptrdiff_t index = output - state->outputs; size_t remaining = (size_t)(state->output_count - index - 1); chroma_log("TRACE", "Removing output %u from array: index=%td, remaining=%zu", id, index, remaining); if (remaining > 0) { memmove(output, output + 1, remaining * sizeof(chroma_output_t)); chroma_log("TRACE", "Shifted %zu outputs in array after removal", remaining); } 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; }