brewery
notashelf /
237a013e0331cc6f497559079d17f4659ceecd8e

chroma

public

Lightweight wallpaper daemon for Wayland

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.git
src/main.cC208 lines5.5 KB
1
#include <errno.h>
2
#include <getopt.h>
3
#include <signal.h>
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <string.h>
7
#include <sys/stat.h>
8
#include <unistd.h>
9
10
#include "../include/chroma.h"
11
12
// Global state for signal handling
13
volatile sig_atomic_t chroma_should_quit = 0;
14
15
static void print_usage(const char *program_name) {
16
  printf("Usage: %s [OPTIONS]\n", program_name);
17
  printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n\n");
18
  printf("Options:\n");
19
  printf("  -c, --config FILE    Configuration file path\n");
20
  printf("  -d, --daemon         Run as daemon\n");
21
  printf("  -v, --verbose        Increase verbosity (can be used multiple "
22
         "times)\n");
23
  printf("                       -v: INFO, -vv: DEBUG, -vvv: TRACE\n");
24
  printf("  -h, --help          Show this help\n");
25
  printf("  --version           Show version information\n");
26
  printf("\nExamples:\n");
27
  printf("  %s -c ~/.config/chroma/chroma.conf\n", program_name);
28
  printf("  %s --daemon\n", program_name);
29
}
30
31
static void print_version(void) {
32
  printf("chroma %s\n", CHROMA_VERSION);
33
  printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n");
34
}
35
36
static int daemonize(void) {
37
  pid_t pid = fork();
38
39
  if (pid < 0) {
40
    chroma_log("ERROR", "fork: %s", strerror(errno));
41
    return -1;
42
  }
43
44
  if (pid > 0) {
45
    // Parent process exits
46
    exit(0);
47
  }
48
49
  // Child process continues
50
  if (setsid() < 0) {
51
    chroma_log("ERROR", "setsid: %s", strerror(errno));
52
    return -1;
53
  }
54
55
  // Change working directory to root
56
  if (chdir("/") < 0) {
57
    chroma_log("ERROR", "chdir: %s", strerror(errno));
58
    return -1;
59
  }
60
61
  // Close standard file descriptors
62
  close(STDIN_FILENO);
63
  close(STDOUT_FILENO);
64
  close(STDERR_FILENO);
65
66
  return 0;
67
}
68
69
static char *get_default_config_path(void) {
70
  static char config_path[MAX_PATH_LEN];
71
  const char *home = getenv("HOME");
72
  const char *xdg_config = getenv("XDG_CONFIG_HOME");
73
74
  if (xdg_config) {
75
    snprintf(config_path, sizeof(config_path), "%s/chroma/%s", xdg_config,
76
             CONFIG_FILE_NAME);
77
  } else if (home) {
78
    snprintf(config_path, sizeof(config_path), "%s/.config/chroma/%s", home,
79
             CONFIG_FILE_NAME);
80
  } else {
81
    strcpy(config_path, CONFIG_FILE_NAME);
82
  }
83
84
  return config_path;
85
}
86
87
int main(int argc, char *argv[]) {
88
  chroma_state_t state;
89
  char *config_file = NULL;
90
  bool daemon_mode = false;
91
  int verbose_level = 0;
92
  int opt;
93
  int ret = 0;
94
95
  static struct option long_options[] = {
96
      {"config", required_argument, 0, 'c'}, {"daemon", no_argument, 0, 'd'},
97
      {"verbose", no_argument, 0, 'v'},      {"help", no_argument, 0, 'h'},
98
      {"version", no_argument, 0, 'V'},      {0, 0, 0, 0}};
99
100
  // Parse command line arguments
101
  while ((opt = getopt_long(argc, argv, "c:dvhV", long_options, NULL)) != -1) {
102
    switch (opt) {
103
    case 'c':
104
      config_file = optarg;
105
      break;
106
    case 'd':
107
      daemon_mode = true;
108
      break;
109
    case 'v':
110
      verbose_level++;
111
      break;
112
    case 'h':
113
      print_usage(argv[0]);
114
      return 0;
115
    case 'V':
116
      print_version();
117
      return 0;
118
    default:
119
      print_usage(argv[0]);
120
      return 1;
121
    }
122
  }
123
124
  // Initialize state
125
  memset(&state, 0, sizeof(state));
126
  state.config.daemon_mode = daemon_mode;
127
128
  // Set log level based on verbosity count
129
  // 0: ERROR+WARN only, 1: +INFO, 2: +DEBUG, 3+: +TRACE
130
  chroma_set_log_level(verbose_level);
131
132
  // Load configuration
133
  if (!config_file) {
134
    config_file = get_default_config_path();
135
  }
136
137
  // Daemonize if requested
138
  if (daemon_mode) {
139
    chroma_log("INFO", "Starting daemon mode");
140
    if (daemonize() != 0) {
141
      fprintf(stderr, "Failed to daemonize\n");
142
      return 1;
143
    }
144
  }
145
146
  // Initialize chroma
147
  chroma_log("INFO", "Initializing chroma wallpaper daemon v%s",
148
             CHROMA_VERSION);
149
  chroma_log_memory_stats("startup");
150
151
  ret = chroma_init(&state);
152
  if (ret != CHROMA_OK) {
153
    chroma_log("ERROR", "Failed to initialize chroma: %s",
154
               chroma_error_string(ret));
155
    chroma_cleanup(&state);
156
    return 1;
157
  }
158
  chroma_log_memory_stats("post-init");
159
160
  // Set up signal handlers after state is initialized
161
  chroma_set_signal_state(&state, config_file);
162
  chroma_handle_signals();
163
164
  // Load configuration
165
  chroma_log("INFO", "Loading configuration from: %s", config_file);
166
  if (chroma_config_load(&state.config, config_file) != CHROMA_OK) {
167
    chroma_log("WARN", "Failed to load config file, using defaults");
168
  }
169
  chroma_log_memory_stats("post-config-load");
170
171
  // Connect to Wayland
172
  ret = chroma_wayland_connect(&state);
173
  if (ret != CHROMA_OK) {
174
    chroma_log("ERROR", "Failed to connect to Wayland: %s",
175
               chroma_error_string(ret));
176
    chroma_cleanup(&state);
177
    return 1;
178
  }
179
  chroma_log_memory_stats("post-wayland-connect");
180
181
  // Initialize EGL
182
  ret = chroma_egl_init(&state);
183
  if (ret != CHROMA_OK) {
184
    chroma_log("ERROR", "Failed to initialize EGL: %s",
185
               chroma_error_string(ret));
186
    chroma_cleanup(&state);
187
    return 1;
188
  }
189
  chroma_log_memory_stats("post-egl-init");
190
191
  chroma_log("INFO", "Chroma daemon initialized successfully");
192
  chroma_log_memory_stats("pre-main-loop");
193
194
  // Main event loop
195
  ret = chroma_run(&state);
196
  if (ret != CHROMA_OK) {
197
    chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret));
198
  }
199
  chroma_log_memory_stats("post-main-loop");
200
201
  // Cleanup
202
  chroma_log("INFO", "Shutting down chroma daemon");
203
  chroma_log_memory_stats("pre-cleanup");
204
  chroma_cleanup(&state);
205
  chroma_log_memory_stats("post-cleanup");
206
207
  return (ret == CHROMA_OK) ? 0 : 1;
208
}