| 1 | #ifndef CHROMA_H |
| 2 | #define CHROMA_H |
| 3 | |
| 4 | #include "wlr-layer-shell-unstable-v1.h" |
| 5 | #include "xdg-shell.h" |
| 6 | #include <signal.h> |
| 7 | #include <stdbool.h> |
| 8 | #include <stdint.h> |
| 9 | #include <sys/types.h> |
| 10 | #include <wayland-client.h> |
| 11 | |
| 12 | #include "chroma_version.h" |
| 13 | |
| 14 | #define MAX_OUTPUTS 16 |
| 15 | #define MAX_PATH_LEN 4096 |
| 16 | #define CONFIG_FILE_NAME "chroma.toml" |
| 17 | |
| 18 | // Log levels |
| 19 | #define CHROMA_LOG_ERROR 0 |
| 20 | #define CHROMA_LOG_WARN 1 |
| 21 | #define CHROMA_LOG_INFO 2 |
| 22 | #define CHROMA_LOG_DEBUG 3 |
| 23 | #define CHROMA_LOG_TRACE 4 |
| 24 | |
| 25 | // Error codes |
| 26 | typedef enum { |
| 27 | CHROMA_OK = 0, |
| 28 | CHROMA_ERROR_INIT = -1, |
| 29 | CHROMA_ERROR_WAYLAND = -2, |
| 30 | CHROMA_ERROR_EGL = -3, |
| 31 | CHROMA_ERROR_IMAGE = -4, |
| 32 | CHROMA_ERROR_CONFIG = -5, |
| 33 | CHROMA_ERROR_MEMORY = -6 |
| 34 | } chroma_error_t; |
| 35 | |
| 36 | // Scaling modes for wallpaper display |
| 37 | typedef enum { |
| 38 | CHROMA_SCALE_FILL = 0, // fill entire output, crop if necessary |
| 39 | CHROMA_SCALE_FIT = 1, // fit image within output, add borders if needed |
| 40 | CHROMA_SCALE_STRETCH = 2, // stretch to fill output, may distort aspect ratio |
| 41 | CHROMA_SCALE_CENTER = 3 // center image at original size |
| 42 | } chroma_scale_mode_t; |
| 43 | |
| 44 | // Anchor positions for wallpaper placement |
| 45 | typedef enum { |
| 46 | CHROMA_ANCHOR_CENTER = 0, // center of the output (default) |
| 47 | CHROMA_ANCHOR_TOP = 1, // top edge, centered horizontally |
| 48 | CHROMA_ANCHOR_BOTTOM = 2, // bottom edge, centered horizontally |
| 49 | CHROMA_ANCHOR_LEFT = 3, // left edge, centered vertically |
| 50 | CHROMA_ANCHOR_RIGHT = 4, // right edge, centered vertically |
| 51 | CHROMA_ANCHOR_TOP_LEFT = 5, // top-left corner |
| 52 | CHROMA_ANCHOR_TOP_RIGHT = 6, // top-right corner |
| 53 | CHROMA_ANCHOR_BOTTOM_LEFT = 7, // bottom-left corner |
| 54 | CHROMA_ANCHOR_BOTTOM_RIGHT = 8 // bottom-right corner |
| 55 | } chroma_anchor_t; |
| 56 | |
| 57 | // Image filtering quality settings |
| 58 | typedef enum { |
| 59 | CHROMA_FILTER_NEAREST = 0, // nearest neighbor filtering (pixelated) |
| 60 | CHROMA_FILTER_LINEAR = 1, // linear filtering (smooth) |
| 61 | CHROMA_FILTER_BILINEAR = 2, // bilinear filtering (smoother) |
| 62 | CHROMA_FILTER_TRILINEAR = 3 // trilinear filtering (smoothest) |
| 63 | } chroma_filter_quality_t; |
| 64 | |
| 65 | // Image data structure |
| 66 | typedef struct { |
| 67 | unsigned char *data; // RGBA pixel data |
| 68 | int width; |
| 69 | int height; |
| 70 | int channels; |
| 71 | char path[MAX_PATH_LEN]; |
| 72 | bool loaded; |
| 73 | bool data_from_stbi; // true if data was allocated by stbi_load |
| 74 | int ref_count; // number of outputs using this image |
| 75 | |
| 76 | // Solid color support |
| 77 | bool is_solid_color; // true if this is a generated solid color |
| 78 | uint32_t solid_color_rgba; // RGBA color value (native byte order) |
| 79 | |
| 80 | // Animation support (for animated GIFs) |
| 81 | bool is_animated; // true if this image has multiple frames |
| 82 | int frame_count; // number of frames |
| 83 | int current_frame; // current frame index |
| 84 | long long last_frame_ms; // timestamp of last frame advance |
| 85 | unsigned char **frames; // array of frame data pointers |
| 86 | int *frame_delays_ms; // delay per frame in milliseconds |
| 87 | int *frame_widths; // width per frame |
| 88 | int *frame_heights; // height per frame |
| 89 | } chroma_image_t; |
| 90 | |
| 91 | // Wayland output information |
| 92 | typedef struct { |
| 93 | struct wl_output *wl_output; |
| 94 | uint32_t id; |
| 95 | int32_t x, y; |
| 96 | int32_t width, height; |
| 97 | int32_t scale; |
| 98 | enum wl_output_transform transform; |
| 99 | char *name; |
| 100 | char *description; |
| 101 | bool active; |
| 102 | |
| 103 | // Back reference to state |
| 104 | struct chroma_state *state; |
| 105 | |
| 106 | // Rendering context |
| 107 | struct wl_surface *surface; |
| 108 | struct zwlr_layer_surface_v1 *layer_surface; |
| 109 | uint32_t configure_serial; |
| 110 | |
| 111 | // Shared memory buffer |
| 112 | struct wl_buffer *shm_buffer; |
| 113 | void *shm_data; |
| 114 | size_t shm_size; |
| 115 | int shm_stride; |
| 116 | |
| 117 | // Associated wallpaper |
| 118 | chroma_image_t *image; |
| 119 | |
| 120 | // Configuration for this output |
| 121 | chroma_scale_mode_t scale_mode; |
| 122 | chroma_filter_quality_t filter_quality; |
| 123 | chroma_anchor_t anchor; |
| 124 | float anchor_x; // custom X offset (0-100, 50=center) |
| 125 | float anchor_y; // custom Y offset (0-100, 50=center) |
| 126 | bool config_loaded; |
| 127 | bool configured; // track if initial configure received |
| 128 | |
| 129 | // Transition support |
| 130 | bool in_transition; // whether crossfade transition is active |
| 131 | long long transition_start_ms; // when transition began |
| 132 | unsigned char *transition_prev; // previous frame pixel data (RGBA) |
| 133 | size_t transition_prev_size; // size of previous frame data |
| 134 | int transition_prev_width; // width of previous frame |
| 135 | int transition_prev_height; // height of previous frame |
| 136 | int transition_duration_ms; // duration for this output's transition |
| 137 | } chroma_output_t; |
| 138 | |
| 139 | // Config mapping structure |
| 140 | typedef struct { |
| 141 | char output_name[256]; |
| 142 | char image_path[MAX_PATH_LEN]; |
| 143 | chroma_scale_mode_t scale_mode; |
| 144 | chroma_filter_quality_t filter_quality; |
| 145 | chroma_anchor_t anchor; |
| 146 | float anchor_x; // custom X offset (0-100, 50=center, 0=left, 100=right) |
| 147 | float anchor_y; // custom Y offset (0-100, 50=center, 0=top, 100=bottom) |
| 148 | } chroma_config_mapping_t; |
| 149 | |
| 150 | // Application configuration |
| 151 | typedef struct { |
| 152 | chroma_config_mapping_t mappings[MAX_OUTPUTS]; |
| 153 | int mapping_count; |
| 154 | char default_image[MAX_PATH_LEN]; |
| 155 | bool daemon_mode; |
| 156 | |
| 157 | // Global scaling and filtering settings (used as defaults) |
| 158 | chroma_scale_mode_t default_scale_mode; |
| 159 | chroma_filter_quality_t default_filter_quality; |
| 160 | chroma_anchor_t default_anchor; |
| 161 | float default_anchor_x; // custom anchor X offset (0-100, 50=center) |
| 162 | float default_anchor_y; // custom anchor Y offset (0-100, 50=center) |
| 163 | |
| 164 | // Image downsampling settings |
| 165 | bool enable_downsampling; // enable automatic downsampling |
| 166 | int max_output_width; // maximum expected output width |
| 167 | int max_output_height; // maximum expected output height |
| 168 | float min_scale_factor; // minimum scale factor (don't scale below this) |
| 169 | |
| 170 | // Solid color background support |
| 171 | // When set via config or IPC, generates a solid color instead of loading an |
| 172 | // image If non-empty, overrides image loading (format: "rrggbb" or |
| 173 | // "rrggbbaa") |
| 174 | char solid_color[10]; |
| 175 | |
| 176 | // Transition settings |
| 177 | bool transition_enabled; // enable smooth crossfade transitions |
| 178 | int transition_duration_ms; // crossfade duration in milliseconds |
| 179 | |
| 180 | // Animation settings |
| 181 | bool animation_enabled; // enable animated GIF playback |
| 182 | int animation_max_fps; // max frames per second for animations |
| 183 | |
| 184 | // IPC settings |
| 185 | bool ipc_enabled; // enable IPC command socket |
| 186 | char ipc_socket_path[MAX_PATH_LEN]; // path to Unix domain socket |
| 187 | } chroma_config_t; |
| 188 | |
| 189 | // Main application state |
| 190 | typedef struct chroma_state { |
| 191 | // Wayland globals |
| 192 | struct wl_display *display; |
| 193 | struct wl_registry *registry; |
| 194 | struct wl_compositor *compositor; |
| 195 | struct zwlr_layer_shell_v1 *layer_shell; |
| 196 | struct wl_shm *shm; |
| 197 | |
| 198 | // Outputs |
| 199 | chroma_output_t outputs[MAX_OUTPUTS]; |
| 200 | int output_count; |
| 201 | |
| 202 | // Images |
| 203 | chroma_image_t images[MAX_OUTPUTS]; |
| 204 | int image_count; |
| 205 | |
| 206 | // Configuration |
| 207 | chroma_config_t config; |
| 208 | |
| 209 | // State flags |
| 210 | bool running; |
| 211 | bool initialized; |
| 212 | |
| 213 | // IPC |
| 214 | int ipc_fd; // IPC socket file descriptor |
| 215 | char config_file[MAX_PATH_LEN]; // active config file path |
| 216 | |
| 217 | // Animation timing |
| 218 | long long last_anim_ms; // timestamp of last animation frame advance |
| 219 | } chroma_state_t; |
| 220 | |
| 221 | // Initialization and cleanup |
| 222 | int chroma_init(chroma_state_t *state); |
| 223 | void chroma_cleanup(chroma_state_t *state); |
| 224 | |
| 225 | // Wayland management |
| 226 | int chroma_wayland_connect(chroma_state_t *state); |
| 227 | void chroma_wayland_disconnect(chroma_state_t *state); |
| 228 | void chroma_registry_listener(void *data, struct wl_registry *registry, |
| 229 | uint32_t id, const char *interface, |
| 230 | uint32_t version); |
| 231 | void chroma_registry_remove(void *data, struct wl_registry *registry, |
| 232 | uint32_t id); |
| 233 | |
| 234 | // Output management |
| 235 | int chroma_output_add(chroma_state_t *state, uint32_t id, |
| 236 | struct wl_output *output); |
| 237 | void chroma_output_remove(chroma_state_t *state, uint32_t id); |
| 238 | chroma_output_t *chroma_output_find_by_id(chroma_state_t *state, uint32_t id); |
| 239 | chroma_output_t *chroma_output_find_by_name(chroma_state_t *state, |
| 240 | const char *name); |
| 241 | |
| 242 | // Output event handlers |
| 243 | void chroma_output_geometry(void *data, struct wl_output *output, int32_t x, |
| 244 | int32_t y, int32_t physical_width, |
| 245 | int32_t physical_height, int32_t subpixel, |
| 246 | const char *make, const char *model, |
| 247 | int32_t transform); |
| 248 | void chroma_output_mode(void *data, struct wl_output *output, uint32_t flags, |
| 249 | int32_t width, int32_t height, int32_t refresh); |
| 250 | void chroma_output_scale(void *data, struct wl_output *output, int32_t scale); |
| 251 | void chroma_output_name(void *data, struct wl_output *output, const char *name); |
| 252 | void chroma_output_description(void *data, struct wl_output *output, |
| 253 | const char *description); |
| 254 | void chroma_output_done(void *data, struct wl_output *output); |
| 255 | |
| 256 | // Rendering and surface management |
| 257 | int chroma_surface_create(chroma_state_t *state, chroma_output_t *output); |
| 258 | void chroma_surface_destroy(chroma_output_t *output); |
| 259 | int chroma_buffer_create(chroma_state_t *state, chroma_output_t *output); |
| 260 | void chroma_buffer_destroy(chroma_output_t *output); |
| 261 | void chroma_buffer_unmap(chroma_output_t *output); |
| 262 | int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output); |
| 263 | |
| 264 | // Layer shell functions |
| 265 | void chroma_layer_surface_configure(void *data, |
| 266 | struct zwlr_layer_surface_v1 *layer_surface, |
| 267 | uint32_t serial, uint32_t width, |
| 268 | uint32_t height); |
| 269 | void chroma_layer_surface_closed(void *data, |
| 270 | struct zwlr_layer_surface_v1 *layer_surface); |
| 271 | |
| 272 | // Image loading |
| 273 | void chroma_image_init_stb(void); |
| 274 | int chroma_image_load(chroma_image_t *image, const char *path, |
| 275 | const chroma_config_t *config, int output_width, |
| 276 | int output_height); |
| 277 | void chroma_image_free(chroma_image_t *image); |
| 278 | void chroma_image_release(chroma_image_t *image); |
| 279 | chroma_image_t *chroma_image_find_by_path(chroma_state_t *state, |
| 280 | const char *path); |
| 281 | chroma_image_t *chroma_image_get_or_load(chroma_state_t *state, |
| 282 | const char *path, int output_width, |
| 283 | int output_height); |
| 284 | int chroma_image_validate(const char *path); |
| 285 | int chroma_image_get_info(const char *path, int *width, int *height, |
| 286 | int *channels); |
| 287 | void chroma_images_cleanup(chroma_state_t *state); |
| 288 | |
| 289 | // Solid color image generation |
| 290 | int chroma_image_load_color(chroma_image_t *image, uint32_t color_rgba, |
| 291 | int width, int height); |
| 292 | |
| 293 | // Additional image format loaders |
| 294 | int chroma_image_load_farbfeld(chroma_image_t *image, const char *path); |
| 295 | int chroma_image_load_svg(chroma_image_t *image, const char *path, |
| 296 | int out_width, int out_height); |
| 297 | int chroma_image_load_webp(chroma_image_t *image, const char *path); |
| 298 | int chroma_image_load_tiff(chroma_image_t *image, const char *path); |
| 299 | int chroma_image_load_avif(chroma_image_t *image, const char *path); |
| 300 | int chroma_image_load_jpegxl(chroma_image_t *image, const char *path); |
| 301 | |
| 302 | // Animation functions |
| 303 | int chroma_anim_load_gif(chroma_image_t *image, const char *path); |
| 304 | void chroma_anim_advance_frame(chroma_image_t *image, |
| 305 | long long current_time_ms); |
| 306 | void chroma_anim_free_frames(chroma_image_t *image); |
| 307 | bool chroma_anim_needs_redraw(chroma_state_t *state); |
| 308 | int chroma_anim_update_outputs(chroma_state_t *state); |
| 309 | |
| 310 | // Transition functions |
| 311 | void chroma_transition_init_output(chroma_output_t *output, int duration_ms); |
| 312 | void chroma_transition_cleanup_output(chroma_output_t *output); |
| 313 | bool chroma_transition_is_active(chroma_output_t *output); |
| 314 | float chroma_transition_get_alpha(chroma_output_t *output, |
| 315 | long long current_time_ms); |
| 316 | bool chroma_transition_needs_redraw(chroma_state_t *state); |
| 317 | int chroma_transition_update_outputs(chroma_state_t *state); |
| 318 | |
| 319 | // IPC functions |
| 320 | int chroma_ipc_init(chroma_state_t *state); |
| 321 | void chroma_ipc_cleanup(chroma_state_t *state); |
| 322 | int chroma_ipc_process(chroma_state_t *state); |
| 323 | int chroma_ipc_get_fd(chroma_state_t *state); |
| 324 | |
| 325 | // Configuration |
| 326 | int chroma_config_load(chroma_config_t *config, const char *config_file); |
| 327 | int chroma_config_load_toml(chroma_config_t *config, const char *config_file); |
| 328 | void chroma_config_free(chroma_config_t *config); |
| 329 | const char *chroma_config_get_image_for_output(chroma_config_t *config, |
| 330 | const char *output_name, |
| 331 | const char *output_description); |
| 332 | int chroma_config_get_mapping_for_output( |
| 333 | chroma_config_t *config, const char *output_name, |
| 334 | const char *output_description, chroma_scale_mode_t *scale_mode, |
| 335 | chroma_filter_quality_t *filter_quality, chroma_anchor_t *anchor, |
| 336 | float *anchor_x, float *anchor_y); |
| 337 | |
| 338 | void chroma_config_print(const chroma_config_t *config); |
| 339 | |
| 340 | // Main loop and events |
| 341 | int chroma_run(chroma_state_t *state); |
| 342 | void chroma_handle_signals(void); |
| 343 | int chroma_reload_config(chroma_state_t *state, const char *config_file); |
| 344 | int chroma_update_outputs(chroma_state_t *state); |
| 345 | void chroma_get_stats(chroma_state_t *state, int *active_outputs, |
| 346 | int *loaded_images); |
| 347 | void handle_output_done(chroma_state_t *state, chroma_output_t *output); |
| 348 | |
| 349 | // Runtime wallpaper control |
| 350 | int chroma_set_wallpaper_for_output(chroma_state_t *state, |
| 351 | const char *output_name, |
| 352 | const char *image_path); |
| 353 | int chroma_set_wallpaper_for_all(chroma_state_t *state, const char *image_path); |
| 354 | int chroma_set_solid_color_for_output(chroma_state_t *state, |
| 355 | const char *output_name, |
| 356 | const char *hex_color); |
| 357 | int chroma_set_solid_color_for_all(chroma_state_t *state, |
| 358 | const char *hex_color); |
| 359 | |
| 360 | // Utilities |
| 361 | void chroma_log(const char *level, const char *format, ...); |
| 362 | const char *chroma_error_string(chroma_error_t error); |
| 363 | void chroma_set_log_level(int level); |
| 364 | int chroma_get_log_level(void); |
| 365 | void chroma_set_signal_state(chroma_state_t *state, const char *config_file); |
| 366 | void chroma_cleanup_signals(void); |
| 367 | char *chroma_expand_path(const char *path); |
| 368 | int chroma_mkdir_recursive(const char *path, mode_t mode); |
| 369 | char *chroma_get_config_dir(void); |
| 370 | bool chroma_path_exists(const char *path); |
| 371 | bool chroma_is_regular_file(const char *path); |
| 372 | bool chroma_is_directory(const char *path); |
| 373 | long chroma_get_file_size(const char *path); |
| 374 | const char *chroma_get_file_extension(const char *path); |
| 375 | long long chroma_get_time_ms(void); |
| 376 | void chroma_sleep_ms(long ms); |
| 377 | void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size); |
| 378 | void chroma_utils_cleanup(void); |
| 379 | |
| 380 | // Memory tracking and logging |
| 381 | void chroma_log_memory_stats(const char *context); |
| 382 | size_t chroma_get_memory_usage(void); |
| 383 | void chroma_log_resource_allocation(const char *resource_type, size_t size, |
| 384 | const char *description); |
| 385 | void chroma_log_resource_deallocation(const char *resource_type, size_t size, |
| 386 | const char *description); |
| 387 | |
| 388 | // Listener structures |
| 389 | extern const struct wl_registry_listener chroma_registry_listener_impl; |
| 390 | extern const struct wl_output_listener chroma_output_listener_impl; |
| 391 | extern const struct zwlr_layer_surface_v1_listener |
| 392 | chroma_layer_surface_listener_impl; |
| 393 | |
| 394 | // Global state for signal handling |
| 395 | extern volatile sig_atomic_t chroma_should_quit; |
| 396 | |
| 397 | #endif // CHROMA_H |