brewery
notashelf /
bc77b887ad02db102d2cbdfd48dc19ad6232143d

chroma

public

Lightweight wallpaper daemon for Wayland

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.git

Commit bc77b887ad02

tarball

NotAShelf <raf@notashelf.dev> · 2025-10-02 18:52 UTC

unverified7 files changed+280-47
various: log memory events

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I6a6a69643b6d00277bb9bcfeb4cd01dc78d7cd3d
diff --git a/include/chroma.h b/include/chroma.hindex ba36ce5..397041f 100644--- a/include/chroma.h+++ b/include/chroma.h@@ -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;diff --git a/src/config.c b/src/config.cindex 7ff8cac..5fcbc9e 100644--- a/src/config.c+++ b/src/config.c@@ -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", "===================="); }diff --git a/src/core.c b/src/core.cindex f07b8ee..8a13255 100644--- a/src/core.c+++ b/src/core.c@@ -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");+  }+}diff --git a/src/image.c b/src/image.cindex e267728..53e03d8 100644--- a/src/image.c+++ b/src/image.c@@ -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 validationdiff --git a/src/main.c b/src/main.cindex d09fb42..9cbd464 100644--- a/src/main.c+++ b/src/main.c@@ -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+}diff --git a/src/render.c b/src/render.cindex dda4030..b73e1ab 100644--- a/src/render.c+++ b/src/render.c@@ -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); } diff --git a/src/wayland.c b/src/wayland.cindex 157cb9d..508ec48 100644--- a/src/wayland.c+++ b/src/wayland.c@@ -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--;