various: log memory events

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I6a6a69643b6d00277bb9bcfeb4cd01dc78d7cd3d
This commit is contained in:
raf 2025-09-30 20:11:06 +03:00
commit bc77b887ad
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
7 changed files with 280 additions and 47 deletions

View file

@ -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", "====================");
}

View file

@ -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");
}
}

View file

@ -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

View file

@ -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;
}
}

View 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);
}

View file

@ -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--;