brewery
notashelf /
e72da82b32d46daf27c4ff663e5f918058b65fd7

chroma

public

Lightweight wallpaper daemon for Wayland

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.git
src/main.cC238 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        Verbose logging\n");
22
  printf("  -h, --help          Show this help\n");
23
  printf("  --version           Show version information\n");
24
  printf("\nExamples:\n");
25
  printf("  %s -c ~/.config/chroma/chroma.conf\n", program_name);
26
  printf("  %s --daemon\n", program_name);
27
}
28
29
static void print_version(void) {
30
  printf("chroma %s\n", CHROMA_VERSION);
31
  printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n");
32
}
33
34
static void signal_handler(int sig) {
35
  switch (sig) {
36
  case SIGTERM:
37
  case SIGINT:
38
    chroma_should_quit = 1;
39
    break;
40
  case SIGHUP:
41
    // TODO: Implement config reload
42
    break;
43
  }
44
}
45
46
static int setup_signals(void) {
47
  struct sigaction sa;
48
49
  memset(&sa, 0, sizeof(sa));
50
  sa.sa_handler = signal_handler;
51
  sigemptyset(&sa.sa_mask);
52
  sa.sa_flags = SA_RESTART;
53
54
  if (sigaction(SIGTERM, &sa, NULL) == -1) {
55
    perror("sigaction(SIGTERM)");
56
    return -1;
57
  }
58
59
  if (sigaction(SIGINT, &sa, NULL) == -1) {
60
    perror("sigaction(SIGINT)");
61
    return -1;
62
  }
63
64
  if (sigaction(SIGHUP, &sa, NULL) == -1) {
65
    perror("sigaction(SIGHUP)");
66
    return -1;
67
  }
68
69
  return 0;
70
}
71
72
static int daemonize(void) {
73
  pid_t pid = fork();
74
75
  if (pid < 0) {
76
    perror("fork");
77
    return -1;
78
  }
79
80
  if (pid > 0) {
81
    // Parent process exits
82
    exit(0);
83
  }
84
85
  // Child process continues
86
  if (setsid() < 0) {
87
    perror("setsid");
88
    return -1;
89
  }
90
91
  // Change working directory to root
92
  if (chdir("/") < 0) {
93
    perror("chdir");
94
    return -1;
95
  }
96
97
  // Close standard file descriptors
98
  close(STDIN_FILENO);
99
  close(STDOUT_FILENO);
100
  close(STDERR_FILENO);
101
102
  return 0;
103
}
104
105
static char *get_default_config_path(void) {
106
  static char config_path[MAX_PATH_LEN];
107
  const char *home = getenv("HOME");
108
  const char *xdg_config = getenv("XDG_CONFIG_HOME");
109
110
  if (xdg_config) {
111
    snprintf(config_path, sizeof(config_path), "%s/chroma/%s", xdg_config,
112
             CONFIG_FILE_NAME);
113
  } else if (home) {
114
    snprintf(config_path, sizeof(config_path), "%s/.config/chroma/%s", home,
115
             CONFIG_FILE_NAME);
116
  } else {
117
    strcpy(config_path, CONFIG_FILE_NAME);
118
  }
119
120
  return config_path;
121
}
122
123
int main(int argc, char *argv[]) {
124
  chroma_state_t state;
125
  char *config_file = NULL;
126
  bool daemon_mode = false;
127
  bool verbose = false;
128
  int opt;
129
  int ret = 0;
130
131
  static struct option long_options[] = {
132
      {"config", required_argument, 0, 'c'}, {"daemon", no_argument, 0, 'd'},
133
      {"verbose", no_argument, 0, 'v'},      {"help", no_argument, 0, 'h'},
134
      {"version", no_argument, 0, 'V'},      {0, 0, 0, 0}};
135
136
  // Parse command line arguments
137
  while ((opt = getopt_long(argc, argv, "c:dvhV", long_options, NULL)) != -1) {
138
    switch (opt) {
139
    case 'c':
140
      config_file = optarg;
141
      break;
142
    case 'd':
143
      daemon_mode = true;
144
      break;
145
    case 'v':
146
      verbose = true;
147
      break;
148
    case 'h':
149
      print_usage(argv[0]);
150
      return 0;
151
    case 'V':
152
      print_version();
153
      return 0;
154
    default:
155
      print_usage(argv[0]);
156
      return 1;
157
    }
158
  }
159
160
  // Initialize state
161
  memset(&state, 0, sizeof(state));
162
  state.config.daemon_mode = daemon_mode;
163
164
  // Set log level based on verbose flag
165
  if (verbose) {
166
    chroma_set_log_level(1); // Enable debug logging
167
  }
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
  ret = chroma_init(&state);
193
  if (ret != CHROMA_OK) {
194
    chroma_log("ERROR", "Failed to initialize chroma: %s",
195
               chroma_error_string(ret));
196
    chroma_cleanup(&state);
197
    return 1;
198
  }
199
200
  // Load configuration
201
  chroma_log("INFO", "Loading configuration from: %s", config_file);
202
  if (chroma_config_load(&state.config, config_file) != CHROMA_OK) {
203
    chroma_log("WARN", "Failed to load config file, using defaults");
204
    // Continue with default configuration
205
  }
206
207
  // Connect to Wayland
208
  ret = chroma_wayland_connect(&state);
209
  if (ret != CHROMA_OK) {
210
    chroma_log("ERROR", "Failed to connect to Wayland: %s",
211
               chroma_error_string(ret));
212
    chroma_cleanup(&state);
213
    return 1;
214
  }
215
216
  // Initialize EGL
217
  ret = chroma_egl_init(&state);
218
  if (ret != CHROMA_OK) {
219
    chroma_log("ERROR", "Failed to initialize EGL: %s",
220
               chroma_error_string(ret));
221
    chroma_cleanup(&state);
222
    return 1;
223
  }
224
225
  chroma_log("INFO", "Chroma daemon initialized successfully");
226
227
  // Main event loop
228
  ret = chroma_run(&state);
229
  if (ret != CHROMA_OK) {
230
    chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret));
231
  }
232
233
  // Cleanup
234
  chroma_log("INFO", "Shutting down chroma daemon");
235
  chroma_cleanup(&state);
236
237
  return (ret == CHROMA_OK) ? 0 : 1;
238
}