notashelf /
a82b986ac6bbb4c02515ebdb4a5c87dd49ba68d4
chroma
publicLightweight wallpaper daemon for Wayland
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.gitCommit a82b986ac6bb
tarballunverified4 files changed+189-29
@@ -44,6 +44,19 @@ typedef enum { CHROMA_SCALE_CENTER = 3 // center image at original size } chroma_scale_mode_t; +// Anchor positions for wallpaper placement+typedef enum {+ CHROMA_ANCHOR_CENTER = 0, // center of the output (default)+ CHROMA_ANCHOR_TOP = 1, // top edge, centered horizontally+ CHROMA_ANCHOR_BOTTOM = 2, // bottom edge, centered horizontally+ CHROMA_ANCHOR_LEFT = 3, // left edge, centered vertically+ CHROMA_ANCHOR_RIGHT = 4, // right edge, centered vertically+ CHROMA_ANCHOR_TOP_LEFT = 5, // top-left corner+ CHROMA_ANCHOR_TOP_RIGHT = 6, // top-right corner+ CHROMA_ANCHOR_BOTTOM_LEFT = 7, // bottom-left corner+ CHROMA_ANCHOR_BOTTOM_RIGHT = 8 // bottom-right corner+} chroma_anchor_t;+ // Image filtering quality settings typedef enum { CHROMA_FILTER_NEAREST = 0, // nearest neighbor filtering (pixelated)@@ -90,6 +103,7 @@ typedef struct { // Configuration for this output chroma_scale_mode_t scale_mode; chroma_filter_quality_t filter_quality;+ chroma_anchor_t anchor; bool config_loaded; // OpenGL resource cache@@ -108,6 +122,7 @@ typedef struct { char image_path[MAX_PATH_LEN]; chroma_scale_mode_t scale_mode; chroma_filter_quality_t filter_quality;+ chroma_anchor_t anchor; } chroma_config_mapping_t; // Application configuration@@ -120,6 +135,7 @@ typedef struct { // Global scaling and filtering settings (used as defaults) chroma_scale_mode_t default_scale_mode; chroma_filter_quality_t default_filter_quality;+ chroma_anchor_t default_anchor; // Image downsampling settings bool enable_downsampling; // enable automatic downsampling@@ -231,7 +247,8 @@ const char *chroma_config_get_image_for_output(chroma_config_t *config, const char *output_name); int chroma_config_get_mapping_for_output( chroma_config_t *config, const char *output_name,- chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality);+ chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality,+ chroma_anchor_t *anchor); void chroma_config_print(const chroma_config_t *config); @@ -6,8 +6,6 @@ #include "../include/chroma.h" -// Default configuration values- static char *trim_whitespace(char *str) { char *end; @@ -122,11 +120,71 @@ static const char *filter_quality_to_string(chroma_filter_quality_t quality) { } } +// Parse anchor position from string+static chroma_anchor_t parse_anchor(const char *value) {+ if (!value)+ return CHROMA_ANCHOR_CENTER;++ if (strcasecmp(value, "center") == 0) {+ return CHROMA_ANCHOR_CENTER;+ } else if (strcasecmp(value, "top") == 0) {+ return CHROMA_ANCHOR_TOP;+ } else if (strcasecmp(value, "bottom") == 0) {+ return CHROMA_ANCHOR_BOTTOM;+ } else if (strcasecmp(value, "left") == 0) {+ return CHROMA_ANCHOR_LEFT;+ } else if (strcasecmp(value, "right") == 0) {+ return CHROMA_ANCHOR_RIGHT;+ } else if (strcasecmp(value, "top-left") == 0 ||+ strcasecmp(value, "topleft") == 0) {+ return CHROMA_ANCHOR_TOP_LEFT;+ } else if (strcasecmp(value, "top-right") == 0 ||+ strcasecmp(value, "topright") == 0) {+ return CHROMA_ANCHOR_TOP_RIGHT;+ } else if (strcasecmp(value, "bottom-left") == 0 ||+ strcasecmp(value, "bottomleft") == 0) {+ return CHROMA_ANCHOR_BOTTOM_LEFT;+ } else if (strcasecmp(value, "bottom-right") == 0 ||+ strcasecmp(value, "bottomright") == 0) {+ return CHROMA_ANCHOR_BOTTOM_RIGHT;+ }++ chroma_log("WARN", "Unknown anchor: %s (using center)", value);+ return CHROMA_ANCHOR_CENTER;+}++// Get string representation of anchor position+static const char *anchor_to_string(chroma_anchor_t anchor) {+ switch (anchor) {+ case CHROMA_ANCHOR_CENTER:+ return "center";+ case CHROMA_ANCHOR_TOP:+ return "top";+ case CHROMA_ANCHOR_BOTTOM:+ return "bottom";+ case CHROMA_ANCHOR_LEFT:+ return "left";+ case CHROMA_ANCHOR_RIGHT:+ return "right";+ case CHROMA_ANCHOR_TOP_LEFT:+ return "top-left";+ case CHROMA_ANCHOR_TOP_RIGHT:+ return "top-right";+ case CHROMA_ANCHOR_BOTTOM_LEFT:+ return "bottom-left";+ case CHROMA_ANCHOR_BOTTOM_RIGHT:+ return "bottom-right";+ default:+ return "unknown";+ }+}+ // Output-to-image mapping static int add_output_mapping(chroma_config_t *config, const char *output_name, const char *image_path, chroma_scale_mode_t scale_mode,- chroma_filter_quality_t filter_quality) {+ chroma_filter_quality_t filter_quality,+ chroma_anchor_t anchor) { if (!config || !output_name || !image_path) { return CHROMA_ERROR_INIT; }@@ -159,12 +217,14 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name, strcpy(mapping->image_path, image_path); mapping->scale_mode = scale_mode; mapping->filter_quality = filter_quality;+ mapping->anchor = anchor; config->mapping_count++; - chroma_log("DEBUG", "Added mapping: %s -> %s (scale: %s, filter: %s)",- output_name, image_path, scale_mode_to_string(scale_mode),- filter_quality_to_string(filter_quality));+ chroma_log(+ "DEBUG", "Added mapping: %s -> %s (scale: %s, filter: %s, anchor: %s)",+ output_name, image_path, scale_mode_to_string(scale_mode),+ filter_quality_to_string(filter_quality), anchor_to_string(anchor)); chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)", config->mapping_count, output_name, image_path, path_len); return CHROMA_OK;@@ -183,6 +243,7 @@ static void init_default_config(chroma_config_t *config) { // Set default scaling and filtering config->default_scale_mode = CHROMA_SCALE_FILL; config->default_filter_quality = CHROMA_FILTER_LINEAR;+ config->default_anchor = CHROMA_ANCHOR_CENTER; // Set default downsampling settings config->enable_downsampling = true; // enable by default, performance etc.@@ -286,6 +347,10 @@ static int parse_config_line(chroma_config_t *config, char *line, config->enable_downsampling = parse_bool(value); chroma_log("DEBUG", "Set downsampling: %s", config->enable_downsampling ? "enabled" : "disabled");+ } else if (strcasecmp(key, "anchor") == 0) {+ config->default_anchor = parse_anchor(value);+ chroma_log("DEBUG", "Set default anchor: %s",+ anchor_to_string(config->default_anchor)); } else if (strcasecmp(key, "max_output_width") == 0) { int width = atoi(value); if (width > 0 && width <= 16384) { // Reasonable limits@@ -351,6 +416,10 @@ static int parse_config_line(chroma_config_t *config, char *line, mapping->filter_quality = parse_filter_quality(value); chroma_log("DEBUG", "Set filter quality for output %s: %s", output_name, filter_quality_to_string(mapping->filter_quality));+ } else if (strcasecmp(property, "anchor") == 0) {+ mapping->anchor = parse_anchor(value);+ chroma_log("DEBUG", "Set anchor for output %s: %s", output_name,+ anchor_to_string(mapping->anchor)); } else { chroma_log("WARN", "Unknown output property: %s (line %d)", property, line_number);@@ -375,7 +444,8 @@ static int parse_config_line(chroma_config_t *config, char *line, if (add_output_mapping(config, output_name, path_to_validate, config->default_scale_mode,- config->default_filter_quality) != CHROMA_OK) {+ config->default_filter_quality,+ config->default_anchor) != CHROMA_OK) { chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name, path_to_validate); if (expanded_path) {@@ -518,12 +588,13 @@ const char *chroma_config_get_image_for_output(chroma_config_t *config, return NULL; } -// Get configuration mapping for output, including scale mode and filter-// quality+// Get configuration mapping for output, including scale mode, filter+// quality, and anchor int chroma_config_get_mapping_for_output( chroma_config_t *config, const char *output_name,- chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality) {- if (!config || !output_name || !scale_mode || !filter_quality) {+ chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality,+ chroma_anchor_t *anchor) {+ if (!config || !output_name || !scale_mode || !filter_quality || !anchor) { return CHROMA_ERROR_INIT; } @@ -532,10 +603,13 @@ int chroma_config_get_mapping_for_output( if (strcmp(config->mappings[i].output_name, output_name) == 0) { *scale_mode = config->mappings[i].scale_mode; *filter_quality = config->mappings[i].filter_quality;+ *anchor = config->mappings[i].anchor; chroma_log("DEBUG",- "Found specific mapping for output %s: scale=%s, filter=%s",+ "Found specific mapping for output %s: scale=%s, filter=%s, "+ "anchor=%s", output_name, scale_mode_to_string(*scale_mode),- filter_quality_to_string(*filter_quality));+ filter_quality_to_string(*filter_quality),+ anchor_to_string(*anchor)); return CHROMA_OK; } }@@ -543,14 +617,14 @@ int chroma_config_get_mapping_for_output( // Return defaults if no specific mapping found *scale_mode = config->default_scale_mode; *filter_quality = config->default_filter_quality;- chroma_log("DEBUG", "Using defaults for output %s: scale=%s, filter=%s",- output_name, scale_mode_to_string(*scale_mode),- filter_quality_to_string(*filter_quality));+ *anchor = config->default_anchor;+ chroma_log(+ "DEBUG", "Using defaults for output %s: scale=%s, filter=%s, anchor=%s",+ output_name, scale_mode_to_string(*scale_mode),+ filter_quality_to_string(*filter_quality), anchor_to_string(*anchor)); return CHROMA_OK; } -- // Print current configuration for debugging void chroma_config_print(const chroma_config_t *config) { if (!config) {@@ -112,19 +112,21 @@ static int assign_wallpaper_to_output(chroma_state_t *state, // Store old configuration values for comparison chroma_scale_mode_t old_scale_mode = output->scale_mode; chroma_filter_quality_t old_filter_quality = output->filter_quality;+ chroma_anchor_t old_anchor = output->anchor; bool had_config = output->config_loaded; - // Load configuration for this output (scale mode and filter quality)+ // Load configuration for this output (scale mode, filter quality, anchor) if (chroma_config_get_mapping_for_output( &state->config, output->name ? output->name : "unknown",- &output->scale_mode, &output->filter_quality) == CHROMA_OK) {+ &output->scale_mode, &output->filter_quality, &output->anchor) == CHROMA_OK) { output->config_loaded = true;- chroma_log("DEBUG", "Loaded config for output %u: scale=%d, filter=%d",- output->id, output->scale_mode, output->filter_quality);+ chroma_log("DEBUG", "Loaded config for output %u: scale=%d, filter=%d, anchor=%d",+ output->id, output->scale_mode, output->filter_quality, output->anchor); // Check if configuration changed and invalidate texture if needed if (had_config && (old_scale_mode != output->scale_mode ||- old_filter_quality != output->filter_quality)) {+ old_filter_quality != output->filter_quality ||+ old_anchor != output->anchor)) { chroma_output_invalidate_texture(output); output->vbo_dirty = true; // VBO needs update for new scale mode chroma_log("DEBUG",@@ -41,8 +41,9 @@ static void get_gl_filter_params(chroma_filter_quality_t quality, } } -// Calculate texture coordinates based on scaling mode+// Calculate texture coordinates based on scaling mode and anchor position static void calculate_texture_coords(chroma_scale_mode_t scale_mode,+ chroma_anchor_t anchor, int image_width, int image_height, int output_width, int output_height, float tex_coords[8]) {@@ -142,6 +143,72 @@ static void calculate_texture_coords(chroma_scale_mode_t scale_mode, break; } + // Apply anchor-based offset by shifting the visible crop region+ // For fill: shift which part of image is shown+ // For fit/center: shift where the image is positioned+ // + // Positive shift reveals left/bottom portion+ // Negative shift reveals right/top portion+ float u_shift = 0.0f;+ float v_shift = 0.0f;++ switch (anchor) {+ case CHROMA_ANCHOR_CENTER:+ u_shift = 0.0f;+ v_shift = 0.0f;+ break;+ case CHROMA_ANCHOR_TOP:+ u_shift = 0.0f;+ v_shift = -0.5f; // shift up to show top of image+ break;+ case CHROMA_ANCHOR_BOTTOM:+ u_shift = 0.0f;+ v_shift = 0.5f; // shift down to show bottom of image+ break;+ case CHROMA_ANCHOR_LEFT:+ u_shift = -0.5f; // shift left to show left of image+ v_shift = 0.0f;+ break;+ case CHROMA_ANCHOR_RIGHT:+ u_shift = 0.5f; // shift right to show right of image+ v_shift = 0.0f;+ break;+ case CHROMA_ANCHOR_TOP_LEFT:+ u_shift = -0.5f;+ v_shift = -0.5f;+ break;+ case CHROMA_ANCHOR_TOP_RIGHT:+ u_shift = 0.5f;+ v_shift = -0.5f;+ break;+ case CHROMA_ANCHOR_BOTTOM_LEFT:+ u_shift = -0.5f;+ v_shift = 0.5f;+ break;+ case CHROMA_ANCHOR_BOTTOM_RIGHT:+ u_shift = 0.5f;+ v_shift = 0.5f;+ break;+ }++ // Calculate the shift amount based on crop/border space available+ float u_crop = 1.0f - (u2 - u1);+ float v_crop = 1.0f - (v2 - v1);+ u1 += u_shift * u_crop;+ u2 += u_shift * u_crop;+ v1 += v_shift * v_crop;+ v2 += v_shift * v_crop;++ // Clamp to valid range [0, 1]+ if (u1 < 0.0f)+ u1 = 0.0f;+ if (u2 > 1.0f)+ u2 = 1.0f;+ if (v1 < 0.0f)+ v1 = 0.0f;+ if (v2 > 1.0f)+ v2 = 1.0f;+ // Set texture coordinates for quad (bottom-left, bottom-right, top-right, // top-left) tex_coords[0] = u1;@@ -688,11 +755,11 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) { // Update VBO only if needed. E.g, image changed, scale mode changed, or first // render if (output->vbo_dirty) {- // Calculate texture coordinates based on scaling mode+ // Calculate texture coordinates based on scaling mode and anchor float tex_coords[8];- calculate_texture_coords(output->scale_mode, output->image->width,- output->image->height, output->width,- output->height, tex_coords);+ calculate_texture_coords(output->scale_mode, output->anchor,+ output->image->width, output->image->height,+ output->width, output->height, tex_coords); // Create dynamic vertex data with calculated texture coordinates float dynamic_vertices[] = {