brewery
notashelf /
ca468ce677b3f6c584b3483120f79f8ed58c9e96

chroma

public

Lightweight wallpaper daemon for Wayland

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