Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6a6a69643b6d00277bb9bcfeb4cd01dc78d7cd3d
248 lines
6 KiB
C
248 lines
6 KiB
C
#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 Increase verbosity (can be used multiple "
|
|
"times)\n");
|
|
printf(" -v: INFO, -vv: DEBUG, -vvv: TRACE\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;
|
|
int verbose_level = 0;
|
|
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_level++;
|
|
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 verbosity count
|
|
// 0: ERROR+WARN only, 1: +INFO, 2: +DEBUG, 3+: +TRACE
|
|
chroma_set_log_level(verbose_level);
|
|
|
|
// 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);
|
|
chroma_log_memory_stats("startup");
|
|
|
|
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;
|
|
}
|
|
chroma_log_memory_stats("post-init");
|
|
|
|
// 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
|
|
}
|
|
chroma_log_memory_stats("post-config-load");
|
|
|
|
// 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;
|
|
}
|
|
chroma_log_memory_stats("post-wayland-connect");
|
|
|
|
// 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_memory_stats("post-egl-init");
|
|
|
|
chroma_log("INFO", "Chroma daemon initialized successfully");
|
|
chroma_log_memory_stats("pre-main-loop");
|
|
|
|
// Main event loop
|
|
ret = chroma_run(&state);
|
|
if (ret != CHROMA_OK) {
|
|
chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret));
|
|
}
|
|
chroma_log_memory_stats("post-main-loop");
|
|
|
|
// Cleanup
|
|
chroma_log("INFO", "Shutting down chroma daemon");
|
|
chroma_log_memory_stats("pre-cleanup");
|
|
chroma_cleanup(&state);
|
|
chroma_log_memory_stats("post-cleanup");
|
|
|
|
return (ret == CHROMA_OK) ? 0 : 1;
|
|
}
|