notashelf /
bc77b887ad02db102d2cbdfd48dc19ad6232143d
chroma
publicLightweight wallpaper daemon for Wayland
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.gitCommit bc77b887ad02
tarballunverified7 files changed+280-47
@@ -17,6 +17,13 @@ #define MAX_PATH_LEN 4096 #define CONFIG_FILE_NAME "chroma.conf" +// Log levels+#define CHROMA_LOG_ERROR 0+#define CHROMA_LOG_WARN 1+#define CHROMA_LOG_INFO 2+#define CHROMA_LOG_DEBUG 3+#define CHROMA_LOG_TRACE 4+ // Error codes typedef enum { CHROMA_OK = 0,@@ -219,6 +226,14 @@ void chroma_sleep_ms(long ms); void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size); void chroma_utils_cleanup(void); +// Memory tracking and logging+void chroma_log_memory_stats(const char *context);+size_t chroma_get_memory_usage(void);+void chroma_log_resource_allocation(const char *resource_type, size_t size,+ const char *description);+void chroma_log_resource_deallocation(const char *resource_type, size_t size,+ const char *description);+ // Listener structures extern const struct wl_registry_listener chroma_registry_listener_impl; extern const struct wl_output_listener chroma_output_listener_impl;@@ -78,6 +78,9 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name, config->mapping_count++; chroma_log("DEBUG", "Added mapping: %s -> %s", output_name, image_path);+ chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)",+ config->mapping_count, output_name, image_path,+ strlen(image_path)); return CHROMA_OK; } @@ -138,11 +141,16 @@ static int parse_config_line(chroma_config_t *config, char *line, strncpy(config->default_image, value, sizeof(config->default_image) - 1); config->default_image[sizeof(config->default_image) - 1] = '\0'; chroma_log("DEBUG", "Set default image: %s", value);+ chroma_log("TRACE", "Default image path set: length=%zu, expanded='%s'",+ strlen(value), value); } else if (strcasecmp(key, "daemon") == 0 || strcasecmp(key, "daemon_mode") == 0) { config->daemon_mode = parse_bool(value); chroma_log("DEBUG", "Set daemon mode: %s", config->daemon_mode ? "true" : "false");+ chroma_log("TRACE",+ "Daemon mode configuration: key='%s', value='%s', parsed=%s",+ key, value, config->daemon_mode ? "true" : "false"); } else if (strncasecmp(key, "output.", 7) == 0) { // Output-specific mapping: e.g., output.DP-1=/path/to/image.jpg const char *output_name = key + 7;@@ -167,6 +175,8 @@ static int parse_config_line(chroma_config_t *config, char *line, } else { chroma_log("WARN", "Unknown configuration key line %d: %s", line_number, key);+ chroma_log("TRACE", "Unrecognized config line %d: key='%s', value='%s'",+ line_number, key, value); } return CHROMA_OK;@@ -200,6 +210,9 @@ int chroma_config_load(chroma_config_t *config, const char *config_file) { } chroma_log("INFO", "Loading configuration from: %s", config_file);+ chroma_log("TRACE",+ "Starting configuration parsing, estimated config size: %ld bytes",+ chroma_get_file_size(config_file)); char line[1024]; int line_number = 0;@@ -229,6 +242,14 @@ int chroma_config_load(chroma_config_t *config, const char *config_file) { "Loaded configuration: %d output mappings, default image: %s", config->mapping_count, config->default_image); + // Log configuration memory usage+ size_t config_size =+ sizeof(chroma_config_t) ++ (config->mapping_count * sizeof(chroma_config_mapping_t));+ chroma_log_resource_allocation("config_data", config_size,+ "configuration structure");+ chroma_log_memory_stats("post-config-load");+ return CHROMA_OK; } @@ -238,6 +259,15 @@ void chroma_config_free(chroma_config_t *config) { return; } + // Log configuration deallocation+ size_t config_size =+ sizeof(chroma_config_t) ++ (config->mapping_count * sizeof(chroma_config_mapping_t));+ chroma_log_resource_deallocation("config_data", config_size,+ "configuration structure");++ chroma_log("TRACE", "Clearing %d output mappings from configuration",+ config->mapping_count); memset(config->mappings, 0, sizeof(config->mappings)); config->mapping_count = 0; @@ -321,6 +351,10 @@ void chroma_config_print(const chroma_config_t *config) { for (int i = 0; i < config->mapping_count; i++) { chroma_log("INFO", " %s -> %s", config->mappings[i].output_name, config->mappings[i].image_path);+ chroma_log(+ "TRACE", " Mapping %d: output='%s', image='%s', path_exists=%s", i,+ config->mappings[i].output_name, config->mappings[i].image_path,+ chroma_path_exists(config->mappings[i].image_path) ? "yes" : "no"); } chroma_log("INFO", "===================="); }@@ -9,8 +9,8 @@ #include "../include/chroma.h" -// Global logging level-static int log_level = 0; // 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG+// Global logging level (using CHROMA_LOG_* constants)+static int log_level = CHROMA_LOG_WARN; // Default to WARN and ERROR only // Initialize chroma state int chroma_init(chroma_state_t *state) {@@ -32,6 +32,8 @@ int chroma_init(chroma_state_t *state) { state->initialized = true; chroma_log("INFO", "Chroma state initialized");+ chroma_log_resource_allocation("chroma_state", sizeof(chroma_state_t),+ "main application state"); return CHROMA_OK; }@@ -43,6 +45,7 @@ void chroma_cleanup(chroma_state_t *state) { } chroma_log("INFO", "Cleaning up chroma state");+ chroma_log_memory_stats("pre-cleanup"); // Stop the main loop state->running = false;@@ -60,6 +63,8 @@ void chroma_cleanup(chroma_state_t *state) { chroma_config_free(&state->config); state->initialized = false;+ chroma_log_resource_deallocation("chroma_state", sizeof(chroma_state_t),+ "main application state"); chroma_log("INFO", "Chroma cleanup complete"); } @@ -116,6 +121,7 @@ static int assign_wallpaper_to_output(chroma_state_t *state, chroma_log("INFO", "Assigned wallpaper to output %u (%s): %s", output->id, output->name ? output->name : "unknown", image_path);+ chroma_log_memory_stats("post-wallpaper-assignment"); return CHROMA_OK; }@@ -173,20 +179,20 @@ static int process_wayland_events(chroma_state_t *state) { return CHROMA_ERROR_WAYLAND; } - /* Dispatch pending events */+ // Dispatch pending events if (wl_display_dispatch_pending(state->display) == -1) { chroma_log("ERROR", "Failed to dispatch pending Wayland events: %s", strerror(errno)); return CHROMA_ERROR_WAYLAND; } - /* Read events from the server */+ // Read events from the server if (wl_display_read_events(state->display) == -1) { chroma_log("ERROR", "Failed to read Wayland events: %s", strerror(errno)); return CHROMA_ERROR_WAYLAND; } - /* Dispatch the read events */+ // Dispatch the read events if (wl_display_dispatch_pending(state->display) == -1) { chroma_log("ERROR", "Failed to dispatch read Wayland events: %s", strerror(errno));@@ -203,6 +209,7 @@ int chroma_run(chroma_state_t *state) { } chroma_log("INFO", "Starting main event loop");+ chroma_log_memory_stats("main-loop-start"); state->running = true; // Initial wallpaper assignment@@ -247,7 +254,7 @@ int chroma_run(chroma_state_t *state) { FD_ZERO(&readfds); FD_SET(fd, &readfds); - timeout.tv_sec = 1; // 1 second timeout+ timeout.tv_sec = 1; // 1s timeout timeout.tv_usec = 0; int select_result = select(fd + 1, &readfds, NULL, NULL, &timeout);@@ -307,24 +314,41 @@ const char *chroma_error_string(chroma_error_t error) { } } -// Logging function+// Convert log level string to numeric level+static int log_level_from_string(const char *level) {+ if (strcmp(level, "ERROR") == 0)+ return CHROMA_LOG_ERROR;+ if (strcmp(level, "WARN") == 0)+ return CHROMA_LOG_WARN;+ if (strcmp(level, "INFO") == 0)+ return CHROMA_LOG_INFO;+ if (strcmp(level, "DEBUG") == 0)+ return CHROMA_LOG_DEBUG;+ if (strcmp(level, "TRACE") == 0)+ return CHROMA_LOG_TRACE;+ return CHROMA_LOG_ERROR; // default to ERROR for unknown levels+}++// Logging function with level filtering void chroma_log(const char *level, const char *format, ...) {+ int msg_level = log_level_from_string(level);+ if (msg_level > log_level) {+ return;+ }+ va_list args; char timestamp[32]; struct timeval tv; struct tm *tm_info; - // Get current time gettimeofday(&tv, NULL); tm_info = localtime(&tv.tv_sec); - // Format timestamp snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday, tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, (int)(tv.tv_usec / 1000)); - // Print log message printf("[%s] %s: ", timestamp, level); va_start(args, format);@@ -445,3 +469,75 @@ void chroma_get_stats(chroma_state_t *state, int *active_outputs, if (loaded_images) *loaded_images = loaded; }++// Get current memory usage from /proc/self/status+// FIXME: some users may choose to confine /proc, we need a cleaner+// way of getting this data in the future+size_t chroma_get_memory_usage(void) {+ FILE *file = fopen("/proc/self/status", "r");+ if (!file) {+ return 0;+ }++ size_t vm_rss = 0;+ char line[256];++ while (fgets(line, sizeof(line), file)) {+ if (strncmp(line, "VmRSS:", 6) == 0) {+ // Parse VmRSS line: "VmRSS: 1234 kB"+ char *ptr = line + 6;+ while (*ptr == ' ' || *ptr == '\t')+ ptr++; // Skip whitespace+ vm_rss = (size_t)strtoul(ptr, NULL, 10) * 1024; // Convert kB to bytes+ break;+ }+ }++ fclose(file);+ return vm_rss;+}++// Log memory statistics with context+void chroma_log_memory_stats(const char *context) {+ size_t memory_usage = chroma_get_memory_usage();+ char mem_str[64];++ chroma_format_memory_size(memory_usage, mem_str, sizeof(mem_str));+ chroma_log("INFO", "Memory usage [%s]: %s", context, mem_str);+}++// Log resource allocation+void chroma_log_resource_allocation(const char *resource_type, size_t size,+ const char *description) {+ if (size > 0) {+ char size_str[64];+ chroma_format_memory_size(size, size_str, sizeof(size_str));+ chroma_log("DEBUG", "Allocated %s: %s (%s)", resource_type, size_str,+ description);+ } else {+ chroma_log("DEBUG", "Allocated %s: %s", resource_type, description);+ }++ // Log memory stats after significant allocations (>1MB)+ if (size > 1024 * 1024) {+ chroma_log_memory_stats("post-allocation");+ }+}++// Log resource deallocation+void chroma_log_resource_deallocation(const char *resource_type, size_t size,+ const char *description) {+ if (size > 0) {+ char size_str[64];+ chroma_format_memory_size(size, size_str, sizeof(size_str));+ chroma_log("DEBUG", "Deallocated %s: %s (%s)", resource_type, size_str,+ description);+ } else {+ chroma_log("DEBUG", "Deallocated %s: %s", resource_type, description);+ }++ // Log memory stats after significant deallocations (>1MB)+ if (size > 1024 * 1024) {+ chroma_log_memory_stats("post-deallocation");+ }+}@@ -1,7 +1,6 @@ #define STB_IMAGE_IMPLEMENTATION #include "../include/stb_image.h" -#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h>@@ -79,10 +78,13 @@ int chroma_image_load(chroma_image_t *image, const char *path) { image->loaded = true; + // Calculate and log memory allocation+ size_t image_size = (size_t)image->width * image->height * image->channels;+ chroma_log_resource_allocation("image_data", image_size, path);+ chroma_log("INFO", "Loaded image: %s (%dx%d, %d channels, %.2f MB)", path, image->width, image->height, image->channels,- (double)(image->width * image->height * image->channels) /- (1024.0 * 1024.0));+ (double)image_size / (1024.0 * 1024.0)); return CHROMA_OK; }@@ -94,6 +96,10 @@ void chroma_image_free(chroma_image_t *image) { } if (image->data) {+ // Log memory deallocation before freeing+ size_t image_size = (size_t)image->width * image->height * image->channels;+ chroma_log_resource_deallocation("image_data", image_size, image->path);+ // Always use stbi_image_free since we load directly with stbi_load stbi_image_free(image->data); image->data = NULL;@@ -218,12 +224,15 @@ void chroma_images_cleanup(chroma_state_t *state) { return; } + chroma_log("DEBUG", "Cleaning up %d images", state->image_count);+ for (int i = 0; i < state->image_count; i++) { chroma_image_free(&state->images[i]); } state->image_count = 0; chroma_log("INFO", "Cleaned up all images");+ chroma_log_memory_stats("post-image-cleanup"); } // Preload common image formats for validation@@ -1,4 +1,3 @@-#include <errno.h> #include <getopt.h> #include <signal.h> #include <stdio.h>@@ -9,7 +8,7 @@ #include "../include/chroma.h" -/* Global state for signal handling */+// Global state for signal handling volatile sig_atomic_t chroma_should_quit = 0; static void print_usage(const char *program_name) {@@ -18,7 +17,9 @@ static void print_usage(const char *program_name) { printf("Options:\n"); printf(" -c, --config FILE Configuration file path\n"); printf(" -d, --daemon Run as daemon\n");- printf(" -v, --verbose Verbose logging\n");+ printf(" -v, --verbose Increase verbosity (can be used multiple "+ "times)\n");+ printf(" -v: INFO, -vv: DEBUG, -vvv: TRACE\n"); printf(" -h, --help Show this help\n"); printf(" --version Show version information\n"); printf("\nExamples:\n");@@ -124,7 +125,7 @@ int main(int argc, char *argv[]) { chroma_state_t state; char *config_file = NULL; bool daemon_mode = false;- bool verbose = false;+ int verbose_level = 0; int opt; int ret = 0; @@ -143,7 +144,7 @@ int main(int argc, char *argv[]) { daemon_mode = true; break; case 'v':- verbose = true;+ verbose_level++; break; case 'h': print_usage(argv[0]);@@ -161,10 +162,9 @@ int main(int argc, char *argv[]) { memset(&state, 0, sizeof(state)); state.config.daemon_mode = daemon_mode; - // Set log level based on verbose flag- if (verbose) {- chroma_set_log_level(1); // Enable debug logging- }+ // Set log level based on verbosity count+ // 0: ERROR+WARN only, 1: +INFO, 2: +DEBUG, 3+: +TRACE+ chroma_set_log_level(verbose_level); // Set up signal handlers if (setup_signals() != 0) {@@ -189,6 +189,8 @@ int main(int argc, char *argv[]) { // Initialize chroma chroma_log("INFO", "Initializing chroma wallpaper daemon v%s", CHROMA_VERSION);+ chroma_log_memory_stats("startup");+ ret = chroma_init(&state); if (ret != CHROMA_OK) { chroma_log("ERROR", "Failed to initialize chroma: %s",@@ -196,6 +198,7 @@ int main(int argc, char *argv[]) { chroma_cleanup(&state); return 1; }+ chroma_log_memory_stats("post-init"); // Load configuration chroma_log("INFO", "Loading configuration from: %s", config_file);@@ -203,6 +206,7 @@ int main(int argc, char *argv[]) { chroma_log("WARN", "Failed to load config file, using defaults"); // Continue with default configuration }+ chroma_log_memory_stats("post-config-load"); // Connect to Wayland ret = chroma_wayland_connect(&state);@@ -212,6 +216,7 @@ int main(int argc, char *argv[]) { chroma_cleanup(&state); return 1; }+ chroma_log_memory_stats("post-wayland-connect"); // Initialize EGL ret = chroma_egl_init(&state);@@ -221,18 +226,23 @@ int main(int argc, char *argv[]) { chroma_cleanup(&state); return 1; }+ chroma_log_memory_stats("post-egl-init"); chroma_log("INFO", "Chroma daemon initialized successfully");+ chroma_log_memory_stats("pre-main-loop"); // Main event loop ret = chroma_run(&state); if (ret != CHROMA_OK) { chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret)); }+ chroma_log_memory_stats("post-main-loop"); // Cleanup chroma_log("INFO", "Shutting down chroma daemon");+ chroma_log_memory_stats("pre-cleanup"); chroma_cleanup(&state);+ chroma_log_memory_stats("post-cleanup"); return (ret == CHROMA_OK) ? 0 : 1;-}\ No newline at end of file+}@@ -156,6 +156,15 @@ static int update_texture_from_image(chroma_output_t *output, // Delete existing texture if it exists if (output->texture_id != 0) {+ // Estimate texture size for logging+ // FIXME: Unfortunately this only works if we have previous image info.+ // Could this b made more accurate?+ if (output->image && output->image->loaded) {+ size_t texture_size = (size_t)output->image->width *+ output->image->height * output->image->channels;+ chroma_log_resource_deallocation("gpu_texture", texture_size,+ "texture replacement");+ } glDeleteTextures(1, &output->texture_id); output->texture_id = 0; }@@ -164,6 +173,10 @@ static int update_texture_from_image(chroma_output_t *output, glGenTextures(1, &output->texture_id); glBindTexture(GL_TEXTURE_2D, output->texture_id); + // Log GPU texture allocation+ size_t texture_size = (size_t)image->width * image->height * image->channels;+ chroma_log_resource_allocation("gpu_texture", texture_size, image->path);+ // Set texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);@@ -207,6 +220,9 @@ static int update_texture_from_image(chroma_output_t *output, "GPU: %s", (double)freed_bytes / (1024.0 * 1024.0), total_using, image->path);+ chroma_log_resource_deallocation("image_data", freed_bytes,+ "post-gpu-upload");+ chroma_log_memory_stats("post-gpu-upload"); } } @@ -222,6 +238,7 @@ static void cleanup_gl_resources(chroma_output_t *output) { } if (output->texture_id != 0) {+ chroma_log_resource_deallocation("gpu_texture", 0, "cleanup"); glDeleteTextures(1, &output->texture_id); output->texture_id = 0; }@@ -296,6 +313,7 @@ int chroma_egl_init(chroma_state_t *state) { } chroma_log("INFO", "EGL initialized: version %d.%d", major, minor);+ chroma_log_memory_stats("post-egl-init"); // Bind OpenGL API if (!eglBindAPI(EGL_OPENGL_API)) {@@ -420,6 +438,11 @@ int chroma_surface_create(chroma_state_t *state, chroma_output_t *output) { chroma_log("INFO", "Created surface for output %u (%dx%d)", output->id, output->width, output->height); + // Log surface creation resource allocation+ size_t surface_size =+ (size_t)output->width * output->height * 4; // estimate RGBA surface+ chroma_log_resource_allocation("egl_surface", surface_size, "output surface");+ return CHROMA_OK; } @@ -452,6 +475,12 @@ void chroma_surface_destroy(chroma_output_t *output) { output->surface = NULL; } + // Log surface destruction+ size_t surface_size =+ (size_t)output->width * output->height * 4; // estimate RGBA surface+ chroma_log_resource_deallocation("egl_surface", surface_size,+ "output surface cleanup");+ chroma_log("DEBUG", "Destroyed surface for output %u", output->id); } @@ -14,6 +14,7 @@ static void registry_global(void *data, struct wl_registry *registry, 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,@@ -22,6 +23,7 @@ static void registry_global(void *data, struct wl_registry *registry, 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 =@@ -31,6 +33,9 @@ static void registry_global(void *data, struct wl_registry *registry, 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(@@ -52,9 +57,10 @@ static void registry_global(void *data, struct wl_registry *registry, 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+ (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); } @@ -63,24 +69,31 @@ const struct wl_registry_listener chroma_registry_listener_impl = { .global_remove = registry_global_remove, }; -/* Layer surface event handlers */+// 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+ (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 */+ // 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 */+ // 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);@@ -89,11 +102,11 @@ static void layer_surface_configure(void *data, 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 */+ (void)layer_surface; chroma_log("INFO", "Layer surface closed for output %u", output->id); - /* Clean up the surface */+ // Clean up the surface if (output->surface) { chroma_surface_destroy(output); }@@ -105,17 +118,17 @@ const struct zwlr_layer_surface_v1_listener chroma_layer_surface_listener_impl = .closed = layer_surface_closed, }; -/* Output event handlers */+// 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+ (void)output;+ (void)subpixel;+ (void)make;+ (void)model; chroma_output->x = x; chroma_output->y = y;@@ -124,24 +137,36 @@ static void output_geometry(void *data, struct wl_output *output, int32_t x, 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; // Unused parameter+ (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; // Unused parameter+ (void)output; chroma_output->scale = scale; chroma_log("DEBUG", "Output %u scale: %d", chroma_output->id, scale);@@ -150,7 +175,7 @@ static void output_scale(void *data, struct wl_output *output, int32_t 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+ (void)output; free(chroma_output->name); chroma_output->name = strdup(name);@@ -165,7 +190,7 @@ static void output_name(void *data, struct wl_output *output, 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+ (void)output; free(chroma_output->description); chroma_output->description = strdup(description);@@ -180,16 +205,23 @@ static void output_description(void *data, struct wl_output *output, static void output_done(void *data, struct wl_output *output) { chroma_output_t *chroma_output = (chroma_output_t *)data;- (void)output; /* Unused parameter */+ (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); } }@@ -203,7 +235,7 @@ const struct wl_output_listener chroma_output_listener_impl = { .done = output_done, }; -/* Wayland connection functions */+// Wayland connection functions int chroma_wayland_connect(chroma_state_t *state) { if (!state) { return CHROMA_ERROR_INIT;@@ -230,11 +262,13 @@ int chroma_wayland_connect(chroma_state_t *state) { 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) {@@ -301,7 +335,7 @@ void chroma_wayland_disconnect(chroma_state_t *state) { chroma_log("INFO", "Disconnected from Wayland display"); } -/* Output management functions */+// Output management functions int chroma_output_add(chroma_state_t *state, uint32_t id, struct wl_output *output) { if (!state || !output) {@@ -318,16 +352,19 @@ int chroma_output_add(chroma_state_t *state, uint32_t id, chroma_output->wl_output = output; chroma_output->id = id;- chroma_output->scale = 1; // Default scale+ 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; } @@ -345,25 +382,28 @@ void chroma_output_remove(chroma_state_t *state, uint32_t id) { chroma_log("INFO", "Removing output %u (%s)", id, output->name ? output->name : "unknown"); - /* Clean up surface if it exists */+ // Clean up surface if it exists if (output->surface) { chroma_surface_destroy(output); } - /* Clean up Wayland output */+ // Clean up Wayland output if (output->wl_output) { wl_output_destroy(output->wl_output); } - /* Free allocated strings */+ // Free allocated strings free(output->name); free(output->description); - /* Remove from array by shifting remaining elements */+ // Remove from array by shifting remaining elements int index = output - state->outputs; int remaining = state->output_count - index - 1;+ chroma_log("TRACE", "Removing output %u from array: index=%d, remaining=%d",+ id, index, remaining); if (remaining > 0) { memmove(output, output + 1, remaining * sizeof(chroma_output_t));+ chroma_log("TRACE", "Shifted %d outputs in array after removal", remaining); } state->output_count--;