initial commit

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I6a6a6964ee9e6ebe436ca8328c6e4a7ec7c9d8d4
This commit is contained in:
raf 2025-09-29 15:12:03 +03:00
commit fcc080871a
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
11 changed files with 12536 additions and 0 deletions

238
src/main.c Normal file
View file

@ -0,0 +1,238 @@
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../include/chroma.h"
/* Global state for signal handling */
volatile sig_atomic_t chroma_should_quit = 0;
static void print_usage(const char *program_name) {
printf("Usage: %s [OPTIONS]\n", program_name);
printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n\n");
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(" -h, --help Show this help\n");
printf(" --version Show version information\n");
printf("\nExamples:\n");
printf(" %s -c ~/.config/chroma/chroma.conf\n", program_name);
printf(" %s --daemon\n", program_name);
}
static void print_version(void) {
printf("chroma %s\n", CHROMA_VERSION);
printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n");
}
static void signal_handler(int sig) {
switch (sig) {
case SIGTERM:
case SIGINT:
chroma_should_quit = 1;
break;
case SIGHUP:
// TODO: Implement config reload
break;
}
}
static int setup_signals(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGTERM, &sa, NULL) == -1) {
perror("sigaction(SIGTERM)");
return -1;
}
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("sigaction(SIGINT)");
return -1;
}
if (sigaction(SIGHUP, &sa, NULL) == -1) {
perror("sigaction(SIGHUP)");
return -1;
}
return 0;
}
static int daemonize(void) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid > 0) {
// Parent process exits
exit(0);
}
// Child process continues
if (setsid() < 0) {
perror("setsid");
return -1;
}
// Change working directory to root
if (chdir("/") < 0) {
perror("chdir");
return -1;
}
// Close standard file descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
return 0;
}
static char *get_default_config_path(void) {
static char config_path[MAX_PATH_LEN];
const char *home = getenv("HOME");
const char *xdg_config = getenv("XDG_CONFIG_HOME");
if (xdg_config) {
snprintf(config_path, sizeof(config_path), "%s/chroma/%s", xdg_config,
CONFIG_FILE_NAME);
} else if (home) {
snprintf(config_path, sizeof(config_path), "%s/.config/chroma/%s", home,
CONFIG_FILE_NAME);
} else {
strcpy(config_path, CONFIG_FILE_NAME);
}
return config_path;
}
int main(int argc, char *argv[]) {
chroma_state_t state;
char *config_file = NULL;
bool daemon_mode = false;
bool verbose = false;
int opt;
int ret = 0;
static struct option long_options[] = {
{"config", required_argument, 0, 'c'}, {"daemon", no_argument, 0, 'd'},
{"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'}, {0, 0, 0, 0}};
// Parse command line arguments
while ((opt = getopt_long(argc, argv, "c:dvhV", long_options, NULL)) != -1) {
switch (opt) {
case 'c':
config_file = optarg;
break;
case 'd':
daemon_mode = true;
break;
case 'v':
verbose = true;
break;
case 'h':
print_usage(argv[0]);
return 0;
case 'V':
print_version();
return 0;
default:
print_usage(argv[0]);
return 1;
}
}
// Initialize state
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 up signal handlers
if (setup_signals() != 0) {
fprintf(stderr, "Failed to set up signal handlers\n");
return 1;
}
// Load configuration
if (!config_file) {
config_file = get_default_config_path();
}
// Daemonize if requested
if (daemon_mode) {
chroma_log("INFO", "Starting daemon mode");
if (daemonize() != 0) {
fprintf(stderr, "Failed to daemonize\n");
return 1;
}
}
// Initialize chroma
chroma_log("INFO", "Initializing chroma wallpaper daemon v%s",
CHROMA_VERSION);
ret = chroma_init(&state);
if (ret != CHROMA_OK) {
chroma_log("ERROR", "Failed to initialize chroma: %s",
chroma_error_string(ret));
chroma_cleanup(&state);
return 1;
}
// Load configuration
chroma_log("INFO", "Loading configuration from: %s", config_file);
if (chroma_config_load(&state.config, config_file) != CHROMA_OK) {
chroma_log("WARN", "Failed to load config file, using defaults");
// Continue with default configuration
}
// Connect to Wayland
ret = chroma_wayland_connect(&state);
if (ret != CHROMA_OK) {
chroma_log("ERROR", "Failed to connect to Wayland: %s",
chroma_error_string(ret));
chroma_cleanup(&state);
return 1;
}
// Initialize EGL
ret = chroma_egl_init(&state);
if (ret != CHROMA_OK) {
chroma_log("ERROR", "Failed to initialize EGL: %s",
chroma_error_string(ret));
chroma_cleanup(&state);
return 1;
}
chroma_log("INFO", "Chroma daemon initialized successfully");
// Main event loop
ret = chroma_run(&state);
if (ret != CHROMA_OK) {
chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret));
}
// Cleanup
chroma_log("INFO", "Shutting down chroma daemon");
chroma_cleanup(&state);
return (ret == CHROMA_OK) ? 0 : 1;
}