| 1 | #ifndef CHROMA_H |
| 2 | #define CHROMA_H |
| 3 | |
| 4 | #include "wlr-layer-shell-unstable-v1.h" |
| 5 | #include "xdg-shell.h" |
| 6 | #include <EGL/egl.h> |
| 7 | #include <GL/gl.h> |
| 8 | #include <signal.h> |
| 9 | #include <stdbool.h> |
| 10 | #include <stdint.h> |
| 11 | #include <sys/types.h> |
| 12 | #include <wayland-client.h> |
| 13 | #include <wayland-egl.h> |
| 14 | |
| 15 | #include "chroma_version.h" |
| 16 | |
| 17 | #define MAX_OUTPUTS 16 |
| 18 | #define MAX_PATH_LEN 4096 |
| 19 | #define CONFIG_FILE_NAME "chroma.conf" |
| 20 | |
| 21 | // Log levels |
| 22 | #define CHROMA_LOG_ERROR 0 |
| 23 | #define CHROMA_LOG_WARN 1 |
| 24 | #define CHROMA_LOG_INFO 2 |
| 25 | #define CHROMA_LOG_DEBUG 3 |
| 26 | #define CHROMA_LOG_TRACE 4 |
| 27 | |
| 28 | // Error codes |
| 29 | typedef enum { |
| 30 | CHROMA_OK = 0, |
| 31 | CHROMA_ERROR_INIT = -1, |
| 32 | CHROMA_ERROR_WAYLAND = -2, |
| 33 | CHROMA_ERROR_EGL = -3, |
| 34 | CHROMA_ERROR_IMAGE = -4, |
| 35 | CHROMA_ERROR_CONFIG = -5, |
| 36 | CHROMA_ERROR_MEMORY = -6 |
| 37 | } chroma_error_t; |
| 38 | |
| 39 | // Scaling modes for wallpaper display |
| 40 | typedef enum { |
| 41 | CHROMA_SCALE_FILL = 0, // fill entire output, crop if necessary |
| 42 | CHROMA_SCALE_FIT = 1, // fit image within output, add borders if needed |
| 43 | CHROMA_SCALE_STRETCH = 2, // stretch to fill output, may distort aspect ratio |
| 44 | CHROMA_SCALE_CENTER = 3 // center image at original size |
| 45 | } chroma_scale_mode_t; |
| 46 | |
| 47 | // Anchor positions for wallpaper placement |
| 48 | typedef enum { |
| 49 | CHROMA_ANCHOR_CENTER = 0, // center of the output (default) |
| 50 | CHROMA_ANCHOR_TOP = 1, // top edge, centered horizontally |
| 51 | CHROMA_ANCHOR_BOTTOM = 2, // bottom edge, centered horizontally |
| 52 | CHROMA_ANCHOR_LEFT = 3, // left edge, centered vertically |
| 53 | CHROMA_ANCHOR_RIGHT = 4, // right edge, centered vertically |
| 54 | CHROMA_ANCHOR_TOP_LEFT = 5, // top-left corner |
| 55 | CHROMA_ANCHOR_TOP_RIGHT = 6, // top-right corner |
| 56 | CHROMA_ANCHOR_BOTTOM_LEFT = 7, // bottom-left corner |
| 57 | CHROMA_ANCHOR_BOTTOM_RIGHT = 8 // bottom-right corner |
| 58 | } chroma_anchor_t; |
| 59 | |
| 60 | // Image filtering quality settings |
| 61 | typedef enum { |
| 62 | CHROMA_FILTER_NEAREST = 0, // nearest neighbor filtering (pixelated) |
| 63 | CHROMA_FILTER_LINEAR = 1, // linear filtering (smooth) |
| 64 | CHROMA_FILTER_BILINEAR = 2, // bilinear filtering (smoother) |
| 65 | CHROMA_FILTER_TRILINEAR = 3 // trilinear filtering (smoothest) |
| 66 | } chroma_filter_quality_t; |
| 67 | |
| 68 | // Image data structure |
| 69 | typedef struct { |
| 70 | unsigned char *data; // RGBA pixel data |
| 71 | int width; |
| 72 | int height; |
| 73 | int channels; |
| 74 | char path[MAX_PATH_LEN]; |
| 75 | bool loaded; |
| 76 | } chroma_image_t; |
| 77 | |
| 78 | // Wayland output information |
| 79 | typedef struct { |
| 80 | struct wl_output *wl_output; |
| 81 | uint32_t id; |
| 82 | int32_t x, y; |
| 83 | int32_t width, height; |
| 84 | int32_t scale; |
| 85 | enum wl_output_transform transform; |
| 86 | char *name; |
| 87 | char *description; |
| 88 | bool active; |
| 89 | |
| 90 | // Back reference to state |
| 91 | struct chroma_state *state; |
| 92 | |
| 93 | // Rendering context |
| 94 | struct wl_surface *surface; |
| 95 | struct zwlr_layer_surface_v1 *layer_surface; |
| 96 | struct wl_egl_window *egl_window; |
| 97 | EGLSurface egl_surface; |
| 98 | uint32_t configure_serial; |
| 99 | |
| 100 | // Associated wallpaper |
| 101 | chroma_image_t *image; |
| 102 | |
| 103 | // Configuration for this output |
| 104 | chroma_scale_mode_t scale_mode; |
| 105 | chroma_filter_quality_t filter_quality; |
| 106 | chroma_anchor_t anchor; |
| 107 | bool config_loaded; |
| 108 | |
| 109 | // OpenGL resource cache |
| 110 | GLuint texture_id; |
| 111 | GLuint shader_program; |
| 112 | GLuint vbo; |
| 113 | GLuint ebo; |
| 114 | bool gl_resources_initialized; |
| 115 | bool texture_uploaded; |
| 116 | bool vbo_dirty; // track VBO needs update |
| 117 | } chroma_output_t; |
| 118 | |
| 119 | // Config mapping structure |
| 120 | typedef struct { |
| 121 | char output_name[256]; |
| 122 | char image_path[MAX_PATH_LEN]; |
| 123 | chroma_scale_mode_t scale_mode; |
| 124 | chroma_filter_quality_t filter_quality; |
| 125 | chroma_anchor_t anchor; |
| 126 | } chroma_config_mapping_t; |
| 127 | |
| 128 | // Application configuration |
| 129 | typedef struct { |
| 130 | chroma_config_mapping_t mappings[MAX_OUTPUTS]; |
| 131 | int mapping_count; |
| 132 | char default_image[MAX_PATH_LEN]; |
| 133 | bool daemon_mode; |
| 134 | |
| 135 | // Global scaling and filtering settings (used as defaults) |
| 136 | chroma_scale_mode_t default_scale_mode; |
| 137 | chroma_filter_quality_t default_filter_quality; |
| 138 | chroma_anchor_t default_anchor; |
| 139 | |
| 140 | // Image downsampling settings |
| 141 | bool enable_downsampling; // enable automatic downsampling |
| 142 | int max_output_width; // maximum expected output width |
| 143 | int max_output_height; // maximum expected output height |
| 144 | float min_scale_factor; // minimum scale factor (don't scale below this) |
| 145 | } chroma_config_t; |
| 146 | |
| 147 | // Main application state |
| 148 | typedef struct chroma_state { |
| 149 | // Wayland globals |
| 150 | struct wl_display *display; |
| 151 | struct wl_registry *registry; |
| 152 | struct wl_compositor *compositor; |
| 153 | struct zwlr_layer_shell_v1 *layer_shell; |
| 154 | |
| 155 | // EGL context |
| 156 | EGLDisplay egl_display; |
| 157 | EGLContext egl_context; |
| 158 | EGLConfig egl_config; |
| 159 | |
| 160 | // Outputs |
| 161 | chroma_output_t outputs[MAX_OUTPUTS]; |
| 162 | int output_count; |
| 163 | |
| 164 | // Images |
| 165 | chroma_image_t images[MAX_OUTPUTS]; |
| 166 | int image_count; |
| 167 | |
| 168 | // Configuration |
| 169 | chroma_config_t config; |
| 170 | |
| 171 | // State flags |
| 172 | bool running; |
| 173 | bool initialized; |
| 174 | } chroma_state_t; |
| 175 | |
| 176 | // Function declarations |
| 177 | |
| 178 | // Initialization and cleanup |
| 179 | int chroma_init(chroma_state_t *state); |
| 180 | void chroma_cleanup(chroma_state_t *state); |
| 181 | |
| 182 | // Wayland management |
| 183 | int chroma_wayland_connect(chroma_state_t *state); |
| 184 | void chroma_wayland_disconnect(chroma_state_t *state); |
| 185 | void chroma_registry_listener(void *data, struct wl_registry *registry, |
| 186 | uint32_t id, const char *interface, |
| 187 | uint32_t version); |
| 188 | void chroma_registry_remove(void *data, struct wl_registry *registry, |
| 189 | uint32_t id); |
| 190 | |
| 191 | // Output management |
| 192 | int chroma_output_add(chroma_state_t *state, uint32_t id, |
| 193 | struct wl_output *output); |
| 194 | void chroma_output_remove(chroma_state_t *state, uint32_t id); |
| 195 | chroma_output_t *chroma_output_find_by_id(chroma_state_t *state, uint32_t id); |
| 196 | chroma_output_t *chroma_output_find_by_name(chroma_state_t *state, |
| 197 | const char *name); |
| 198 | |
| 199 | // Output event handlers |
| 200 | void chroma_output_geometry(void *data, struct wl_output *output, int32_t x, |
| 201 | int32_t y, int32_t physical_width, |
| 202 | int32_t physical_height, int32_t subpixel, |
| 203 | const char *make, const char *model, |
| 204 | int32_t transform); |
| 205 | void chroma_output_mode(void *data, struct wl_output *output, uint32_t flags, |
| 206 | int32_t width, int32_t height, int32_t refresh); |
| 207 | void chroma_output_scale(void *data, struct wl_output *output, int32_t scale); |
| 208 | void chroma_output_name(void *data, struct wl_output *output, const char *name); |
| 209 | void chroma_output_description(void *data, struct wl_output *output, |
| 210 | const char *description); |
| 211 | void chroma_output_done(void *data, struct wl_output *output); |
| 212 | |
| 213 | // EGL and rendering |
| 214 | int chroma_egl_init(chroma_state_t *state); |
| 215 | void chroma_egl_cleanup(chroma_state_t *state); |
| 216 | int chroma_surface_create(chroma_state_t *state, chroma_output_t *output); |
| 217 | void chroma_surface_destroy(chroma_output_t *output); |
| 218 | int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output); |
| 219 | void chroma_output_invalidate_texture(chroma_output_t *output); |
| 220 | |
| 221 | // Layer shell functions |
| 222 | void chroma_layer_surface_configure(void *data, |
| 223 | struct zwlr_layer_surface_v1 *layer_surface, |
| 224 | uint32_t serial, uint32_t width, |
| 225 | uint32_t height); |
| 226 | void chroma_layer_surface_closed(void *data, |
| 227 | struct zwlr_layer_surface_v1 *layer_surface); |
| 228 | |
| 229 | // Image loading |
| 230 | void chroma_image_init_stb(void); |
| 231 | int chroma_image_load(chroma_image_t *image, const char *path, |
| 232 | const chroma_config_t *config); |
| 233 | void chroma_image_free(chroma_image_t *image); |
| 234 | chroma_image_t *chroma_image_find_by_path(chroma_state_t *state, |
| 235 | const char *path); |
| 236 | chroma_image_t *chroma_image_get_or_load(chroma_state_t *state, |
| 237 | const char *path); |
| 238 | int chroma_image_validate(const char *path); |
| 239 | int chroma_image_get_info(const char *path, int *width, int *height, |
| 240 | int *channels); |
| 241 | void chroma_images_cleanup(chroma_state_t *state); |
| 242 | |
| 243 | // Configuration |
| 244 | int chroma_config_load(chroma_config_t *config, const char *config_file); |
| 245 | void chroma_config_free(chroma_config_t *config); |
| 246 | const char *chroma_config_get_image_for_output(chroma_config_t *config, |
| 247 | const char *output_name); |
| 248 | int chroma_config_get_mapping_for_output( |
| 249 | chroma_config_t *config, const char *output_name, |
| 250 | chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality, |
| 251 | chroma_anchor_t *anchor); |
| 252 | |
| 253 | void chroma_config_print(const chroma_config_t *config); |
| 254 | |
| 255 | // Main loop and events |
| 256 | int chroma_run(chroma_state_t *state); |
| 257 | void chroma_handle_signals(void); |
| 258 | int chroma_reload_config(chroma_state_t *state, const char *config_file); |
| 259 | int chroma_update_outputs(chroma_state_t *state); |
| 260 | void chroma_get_stats(chroma_state_t *state, int *active_outputs, |
| 261 | int *loaded_images); |
| 262 | void handle_output_done(chroma_state_t *state, chroma_output_t *output); |
| 263 | |
| 264 | // Utilities |
| 265 | void chroma_log(const char *level, const char *format, ...); |
| 266 | const char *chroma_error_string(chroma_error_t error); |
| 267 | void chroma_set_log_level(int level); |
| 268 | int chroma_get_log_level(void); |
| 269 | void chroma_set_signal_state(chroma_state_t *state, const char *config_file); |
| 270 | void chroma_cleanup_signals(void); |
| 271 | char *chroma_expand_path(const char *path); |
| 272 | int chroma_mkdir_recursive(const char *path, mode_t mode); |
| 273 | char *chroma_get_config_dir(void); |
| 274 | bool chroma_path_exists(const char *path); |
| 275 | bool chroma_is_regular_file(const char *path); |
| 276 | bool chroma_is_directory(const char *path); |
| 277 | long chroma_get_file_size(const char *path); |
| 278 | const char *chroma_get_file_extension(const char *path); |
| 279 | int chroma_strcasecmp(const char *s1, const char *s2); |
| 280 | size_t chroma_strlcpy(char *dst, const char *src, size_t size); |
| 281 | size_t chroma_strlcat(char *dst, const char *src, size_t size); |
| 282 | long long chroma_get_time_ms(void); |
| 283 | void chroma_sleep_ms(long ms); |
| 284 | void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size); |
| 285 | void chroma_utils_cleanup(void); |
| 286 | |
| 287 | // Memory tracking and logging |
| 288 | void chroma_log_memory_stats(const char *context); |
| 289 | size_t chroma_get_memory_usage(void); |
| 290 | void chroma_log_resource_allocation(const char *resource_type, size_t size, |
| 291 | const char *description); |
| 292 | void chroma_log_resource_deallocation(const char *resource_type, size_t size, |
| 293 | const char *description); |
| 294 | |
| 295 | // Listener structures |
| 296 | extern const struct wl_registry_listener chroma_registry_listener_impl; |
| 297 | extern const struct wl_output_listener chroma_output_listener_impl; |
| 298 | extern const struct zwlr_layer_surface_v1_listener |
| 299 | chroma_layer_surface_listener_impl; |
| 300 | |
| 301 | // Global state for signal handling |
| 302 | extern volatile sig_atomic_t chroma_should_quit; |
| 303 | |
| 304 | #endif // CHROMA_H |