initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6a6a6964ee9e6ebe436ca8328c6e4a7ec7c9d8d4
This commit is contained in:
commit
fcc080871a
11 changed files with 12536 additions and 0 deletions
439
src/core.c
Normal file
439
src/core.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/chroma.h"
|
||||
|
||||
// Global logging level
|
||||
static int log_level = 0; // 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG
|
||||
|
||||
// Initialize chroma state
|
||||
int chroma_init(chroma_state_t *state) {
|
||||
if (!state) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
// Initialize all fields to zero
|
||||
memset(state, 0, sizeof(chroma_state_t));
|
||||
|
||||
// Set initial state
|
||||
state->running = false;
|
||||
state->initialized = false;
|
||||
state->egl_display = EGL_NO_DISPLAY;
|
||||
state->egl_context = EGL_NO_CONTEXT;
|
||||
|
||||
// Initialize stb_image
|
||||
chroma_image_init_stb();
|
||||
|
||||
state->initialized = true;
|
||||
chroma_log("INFO", "Chroma state initialized");
|
||||
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Cleanup chroma state
|
||||
void chroma_cleanup(chroma_state_t *state) {
|
||||
if (!state || !state->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Cleaning up chroma state");
|
||||
|
||||
// Stop the main loop
|
||||
state->running = false;
|
||||
|
||||
// Clean up all images
|
||||
chroma_images_cleanup(state);
|
||||
|
||||
// Clean up EGL
|
||||
chroma_egl_cleanup(state);
|
||||
|
||||
// Clean up Wayland
|
||||
chroma_wayland_disconnect(state);
|
||||
|
||||
// Clean up configuration
|
||||
chroma_config_free(&state->config);
|
||||
|
||||
state->initialized = false;
|
||||
chroma_log("INFO", "Chroma cleanup complete");
|
||||
}
|
||||
|
||||
// Assign wallpaper to an output
|
||||
static int assign_wallpaper_to_output(chroma_state_t *state,
|
||||
chroma_output_t *output) {
|
||||
if (!state || !output || !output->active) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
// Get image path for this output
|
||||
const char *image_path = chroma_config_get_image_for_output(
|
||||
&state->config, output->name ? output->name : "unknown");
|
||||
if (!image_path) {
|
||||
chroma_log("WARN", "No wallpaper configured for output %u (%s)", output->id,
|
||||
output->name ? output->name : "unknown");
|
||||
return CHROMA_ERROR_CONFIG;
|
||||
}
|
||||
|
||||
// Load or get cached image
|
||||
chroma_image_t *image = chroma_image_get_or_load(state, image_path);
|
||||
if (!image) {
|
||||
chroma_log("ERROR", "Failed to load image for output %u: %s", output->id,
|
||||
image_path);
|
||||
return CHROMA_ERROR_IMAGE;
|
||||
}
|
||||
|
||||
// Assign image to output
|
||||
output->image = image;
|
||||
|
||||
// Create surface if it doesn't exist
|
||||
if (!output->surface) {
|
||||
int ret = chroma_surface_create(state, output);
|
||||
if (ret != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to create surface for output %u", output->id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Render wallpaper
|
||||
int ret = chroma_render_wallpaper(state, output);
|
||||
if (ret != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to render wallpaper for output %u", output->id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Assigned wallpaper to output %u (%s): %s", output->id,
|
||||
output->name ? output->name : "unknown", image_path);
|
||||
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Assign wallpapers to all active outputs
|
||||
static int assign_wallpapers_to_all_outputs(chroma_state_t *state) {
|
||||
if (!state) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
int success_count = 0;
|
||||
int error_count = 0;
|
||||
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
chroma_output_t *output = &state->outputs[i];
|
||||
|
||||
if (!output->active) {
|
||||
chroma_log("DEBUG", "Skipping inactive output %u", output->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assign_wallpaper_to_output(state, output) == CHROMA_OK) {
|
||||
success_count++;
|
||||
} else {
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Wallpaper assignment complete: %d success, %d errors",
|
||||
success_count, error_count);
|
||||
|
||||
return (success_count > 0) ? CHROMA_OK : CHROMA_ERROR_IMAGE;
|
||||
}
|
||||
|
||||
// Handle output configuration complete event
|
||||
void handle_output_done(chroma_state_t *state, chroma_output_t *output) {
|
||||
if (!state || !output) {
|
||||
return;
|
||||
}
|
||||
|
||||
chroma_log("INFO",
|
||||
"Output %u (%s) configuration complete: %dx%d@%d, scale=%d",
|
||||
output->id, output->name ? output->name : "unknown", output->width,
|
||||
output->height, 0, output->scale);
|
||||
|
||||
/* Assign wallpaper to this output */
|
||||
if (assign_wallpaper_to_output(state, output) != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to assign wallpaper to output %u", output->id);
|
||||
}
|
||||
}
|
||||
|
||||
// Process Wayland events
|
||||
static int process_wayland_events(chroma_state_t *state) {
|
||||
if (!state || !state->display) {
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
if (wl_display_dispatch_pending(state->display) == -1) {
|
||||
chroma_log("ERROR", "Failed to dispatch read Wayland events: %s",
|
||||
strerror(errno));
|
||||
return CHROMA_ERROR_WAYLAND;
|
||||
}
|
||||
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Main event loop
|
||||
int chroma_run(chroma_state_t *state) {
|
||||
if (!state || !state->initialized) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Starting main event loop");
|
||||
state->running = true;
|
||||
|
||||
// Initial wallpaper assignment
|
||||
chroma_log("INFO", "Performing initial wallpaper assignment");
|
||||
assign_wallpapers_to_all_outputs(state);
|
||||
|
||||
// Main event loop
|
||||
while (state->running && !chroma_should_quit) {
|
||||
// Dispatch any pending events first
|
||||
if (wl_display_dispatch_pending(state->display) == -1) {
|
||||
chroma_log("ERROR", "Failed to dispatch pending events: %s",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare to read events
|
||||
if (wl_display_prepare_read(state->display) == -1) {
|
||||
chroma_log("ERROR", "Failed to prepare Wayland display for reading");
|
||||
break;
|
||||
}
|
||||
|
||||
// Flush outgoing requests
|
||||
if (wl_display_flush(state->display) == -1) {
|
||||
chroma_log("ERROR", "Failed to flush Wayland display: %s",
|
||||
strerror(errno));
|
||||
wl_display_cancel_read(state->display);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the display file descriptor
|
||||
int fd = wl_display_get_fd(state->display);
|
||||
if (fd == -1) {
|
||||
chroma_log("ERROR", "Failed to get Wayland display file descriptor");
|
||||
wl_display_cancel_read(state->display);
|
||||
break;
|
||||
}
|
||||
|
||||
// Use select() to wait for events with timeout
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
timeout.tv_sec = 1; // 1 second timeout
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int select_result = select(fd + 1, &readfds, NULL, NULL, &timeout);
|
||||
|
||||
if (select_result == -1) {
|
||||
if (errno == EINTR) {
|
||||
// Interrupted by signal, check if we should quit
|
||||
wl_display_cancel_read(state->display);
|
||||
continue;
|
||||
}
|
||||
chroma_log("ERROR", "select() failed: %s", strerror(errno));
|
||||
wl_display_cancel_read(state->display);
|
||||
break;
|
||||
}
|
||||
|
||||
if (select_result == 0) {
|
||||
// Timeout - no events available
|
||||
wl_display_cancel_read(state->display);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Events are available
|
||||
if (FD_ISSET(fd, &readfds)) {
|
||||
if (process_wayland_events(state) != CHROMA_OK) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
wl_display_cancel_read(state->display);
|
||||
}
|
||||
}
|
||||
|
||||
state->running = false;
|
||||
chroma_log("INFO", "Main event loop ended");
|
||||
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Error code to string conversion
|
||||
const char *chroma_error_string(chroma_error_t error) {
|
||||
switch (error) {
|
||||
case CHROMA_OK:
|
||||
return "Success";
|
||||
case CHROMA_ERROR_INIT:
|
||||
return "Initialization error";
|
||||
case CHROMA_ERROR_WAYLAND:
|
||||
return "Wayland error";
|
||||
case CHROMA_ERROR_EGL:
|
||||
return "EGL error";
|
||||
case CHROMA_ERROR_IMAGE:
|
||||
return "Image loading error";
|
||||
case CHROMA_ERROR_CONFIG:
|
||||
return "Configuration error";
|
||||
case CHROMA_ERROR_MEMORY:
|
||||
return "Memory allocation error";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
// Logging function
|
||||
void chroma_log(const char *level, const char *format, ...) {
|
||||
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);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Set log level
|
||||
void chroma_set_log_level(int level) { log_level = level; }
|
||||
|
||||
// Get log level
|
||||
int chroma_get_log_level(void) { return log_level; }
|
||||
|
||||
// Handle configuration reload (SIGHUP)
|
||||
int chroma_reload_config(chroma_state_t *state, const char *config_file) {
|
||||
if (!state) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Reloading configuration");
|
||||
|
||||
// Free current configuration
|
||||
chroma_config_free(&state->config);
|
||||
|
||||
// Load new configuration
|
||||
int ret = chroma_config_load(&state->config, config_file);
|
||||
if (ret != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to reload configuration: %s",
|
||||
chroma_error_string(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reassign wallpapers with new configuration
|
||||
ret = assign_wallpapers_to_all_outputs(state);
|
||||
if (ret != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to reassign wallpapers after config reload");
|
||||
return ret;
|
||||
}
|
||||
|
||||
chroma_log("INFO", "Configuration reloaded successfully");
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Check if an output needs wallpaper update
|
||||
static bool output_needs_update(chroma_output_t *output) {
|
||||
if (!output || !output->active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if output has no surface or image assigned
|
||||
if (!output->surface || !output->image) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if image is no longer loaded
|
||||
if (!output->image->loaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update outputs that need wallpaper refresh
|
||||
int chroma_update_outputs(chroma_state_t *state) {
|
||||
if (!state) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
int updated_count = 0;
|
||||
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
chroma_output_t *output = &state->outputs[i];
|
||||
|
||||
if (output_needs_update(output)) {
|
||||
if (assign_wallpaper_to_output(state, output) == CHROMA_OK) {
|
||||
updated_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_count > 0) {
|
||||
chroma_log("INFO", "Updated wallpapers for %d outputs", updated_count);
|
||||
}
|
||||
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
// Get statistics
|
||||
void chroma_get_stats(chroma_state_t *state, int *active_outputs,
|
||||
int *loaded_images) {
|
||||
if (!state) {
|
||||
if (active_outputs)
|
||||
*active_outputs = 0;
|
||||
if (loaded_images)
|
||||
*loaded_images = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int active = 0;
|
||||
for (int i = 0; i < state->output_count; i++) {
|
||||
if (state->outputs[i].active) {
|
||||
active++;
|
||||
}
|
||||
}
|
||||
|
||||
int loaded = 0;
|
||||
for (int i = 0; i < state->image_count; i++) {
|
||||
if (state->images[i].loaded) {
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
if (active_outputs)
|
||||
*active_outputs = active;
|
||||
if (loaded_images)
|
||||
*loaded_images = loaded;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue