Dunst
Lightweight notification daemon
Loading...
Searching...
No Matches
settings.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause */
8
9#include "settings.h"
10
11#include <dirent.h>
12#include <errno.h>
13#include <fnmatch.h>
14#include <glib.h>
15#include <stdio.h>
16#include <string.h>
17
18#include "dunst.h"
19#include "log.h"
20#include "option_parser.h"
21#include "utils.h"
22
23#ifndef SYSCONFDIR
28#define SYSCONFDIR "/usr/local/etc/xdg"
29#endif
30
31struct settings settings;
32bool print_notifications = false;
33
43static int is_drop_in(const struct dirent *dent) {
44 return 0 == fnmatch("*.conf", dent->d_name, FNM_PATHNAME | FNM_PERIOD)
45 ? 1 // success
46 : 0;
47}
48
60static GPtrArray *get_xdg_conf_basedirs(void) {
61 GPtrArray *arr = g_ptr_array_new_full(4, g_free);
62 g_ptr_array_add(arr, g_build_filename(g_get_user_config_dir(), "dunst", NULL));
63
73 add_paths_from_env(arr, "XDG_CONFIG_DIRS", "dunst", SYSCONFDIR);
74 return arr;
75}
76
77static void config_files_add_drop_ins(GPtrArray *config_files, const char *path) {
78 int insert_index = config_files->len;
79 if (insert_index == 0) {
80 // there is no base config file
81 return;
82 }
83 char *drop_in_dir = g_strconcat(path, ".d", NULL);
84 struct dirent **drop_ins = NULL;
85 int n = scandir(drop_in_dir, &drop_ins, is_drop_in, alphasort);
86
87 if (n == -1) {
88 // Scandir error. Most likely the directory doesn't exist.
89 g_free(drop_in_dir);
90 return;
91 }
92
93 while (n--) {
94 char *drop_in = g_strconcat(drop_in_dir, "/",
95 drop_ins[n]->d_name, NULL);
96 LOG_D("Found drop-in: %s\n", drop_in);
97 g_ptr_array_insert(config_files, insert_index, drop_in);
98 g_free(drop_ins[n]);
99 }
100
101 g_free(drop_in_dir);
102 g_free(drop_ins);
103}
104
113static GPtrArray* get_conf_files(void) {
114 GPtrArray *config_locations = get_xdg_conf_basedirs();
115 GPtrArray *config_files = g_ptr_array_new_full(3, g_free);
116 char *dunstrc_location = NULL;
117 for (size_t i = 0; i < config_locations->len; i++) {
118 dunstrc_location = g_build_filename(config_locations->pdata[i],
119 "dunstrc", NULL);
120 LOG_D("Trying config location: %s", dunstrc_location);
121 if (is_readable_file(dunstrc_location)) {
122 g_ptr_array_add(config_files, dunstrc_location);
123 break;
124 }
125 }
126
127 config_files_add_drop_ins(config_files, dunstrc_location);
128
129 g_ptr_array_unref(config_locations);
130 return config_files;
131}
132
133FILE *fopen_conf(char * const path)
134{
135 FILE *f = NULL;
136 char *real_path = string_to_path(g_strdup(path));
137
138 if (is_readable_file(real_path) && NULL != (f = fopen(real_path, "r")))
139 LOG_I(MSG_FOPEN_SUCCESS(path, f));
140 else
141 LOG_W(MSG_FOPEN_FAILURE(path));
142
143 g_free(real_path);
144 return f;
145}
146
147void check_and_correct_settings(struct settings *s) {
148 bool on_wayland = is_running_wayland();
149
150#ifndef ENABLE_WAYLAND
151 if (on_wayland) {
152 /* We are using xwayland now. Setting force_xwayland to make sure
153 * the idle workaround below is activated */
154 settings.force_xwayland = true;
155 }
156#endif
157
158 if (settings.force_xwayland && on_wayland) {
159 if (settings.idle_threshold > 0)
160 LOG_W("Using xwayland. Disabling idle.");
161 /* There is no way to detect if the user is idle
162 * on xwayland, so turn this feature off */
163 settings.idle_threshold = 0;
164 }
165
166 // check sanity of the progress bar options
167 {
168 if (s->progress_bar_height < (2 * s->progress_bar_frame_width)) {
169 DIE("setting progress_bar_frame_width is bigger than half of progress_bar_height");
170 }
171 if (s->progress_bar_max_width < (2 * s->progress_bar_frame_width)) {
172 DIE("setting progress_bar_frame_width is bigger than half of progress_bar_max_width");
173 }
174 if (s->progress_bar_max_width < s->progress_bar_min_width) {
175 DIE("setting progress_bar_max_width is smaller than progress_bar_min_width");
176 }
177 if (s->progress_bar_min_width > s->width.max) {
178 LOG_W("Progress bar min width is greater than the max width of the notification");
179 }
180 int progress_bar_max_corner_radius = (s->progress_bar_height / 2);
181 if (s->progress_bar_corner_radius > progress_bar_max_corner_radius) {
182 settings.progress_bar_corner_radius = progress_bar_max_corner_radius;
183 LOG_W("Progress bar corner radius clamped to half of progress bar height (%i)",
184 progress_bar_max_corner_radius);
185 }
186 }
187
188 // check lengths
189 if (s->width.min == INT_MIN) {
190 s->width.min = 0;
191 }
192 if (s->width.min < 0 || s->width.max < 0) {
193 DIE("setting width does not support negative values");
194 }
195 if (s->width.min > s->width.max) {
196 DIE("setting width min (%i) is always greather than max (%i)", s->width.min, s->width.max);
197 }
198
199 if (s->height.min == INT_MIN) {
200 s->height.min = 0;
201 }
202 if (s->height.min < 0 || s->height.max < 0) {
203 DIE("setting height does not support negative values");
204 }
205 if (s->height.min > s->height.max) {
206 DIE("setting height min (%i) is always greather than max (%i)", s->height.min, s->height.max);
207 }
208
209 if (s->offset.x == INT_MIN || s->offset.y == INT_MAX) {
210 DIE("setting offset needs both horizontal and vertical values");
211 }
212
213 // TODO Implement this with icon sizes as rules
214
215 // restrict the icon size to a reasonable limit if we have a fixed width.
216 // Otherwise the layout will be broken by too large icons.
217 // See https://github.com/dunst-project/dunst/issues/540
218 // if (s->width.max > 0) {
219 // const int icon_size_limit = s->width.max / 2;
220 // if ( s->max_icon_size == 0
221 // || s->max_icon_size > icon_size_limit) {
222 // if (s->max_icon_size != 0) {
223 // LOG_W("Max width was set to %d but got a max_icon_size of %d, too large to use. Setting max_icon_size=%d",
224 // s->width.max, s->max_icon_size, icon_size_limit);
225 // } else {
226 // LOG_I("Max width was set but max_icon_size is unlimited. Limiting icons to %d pixels", icon_size_limit);
227 // }
228
229 // s->max_icon_size = icon_size_limit;
230 // }
231 // }
232
233 // int text_icon_padding = settings.text_icon_padding != 0 ? settings.text_icon_padding : settings.h_padding;
234 // int max_text_width = settings.width.max - settings.max_icon_size - text_icon_padding - 2 * settings.h_padding;
235 // if (max_text_width < 10) {
236 // DIE("max_icon_size and horizontal padding are too large for the given width");
237 // }
238
239}
240
241static void process_conf_file(const gpointer conf_fname, gpointer n_success) {
242 const gchar * const p = conf_fname;
243
244 LOG_D("Reading config file '%s'", p);
245 /* Check for "-" here, so the file handling stays in one place */
246 FILE *f = STR_EQ(p, "-") ? stdin : fopen_verbose(p);
247 if (!f)
248 return;
249
250 struct ini *ini = load_ini_file(f);
251 fclose(f);
252
253 LOG_D("Loading settings");
254 save_settings(ini);
255
256 LOG_D("Checking/correcting settings");
257 check_and_correct_settings(&settings);
258
259 finish_ini(ini);
260 g_free(ini);
261
262 ++(*(int *) n_success);
263}
264
265void load_settings(char **const paths)
266{
267 LOG_D("Setting defaults");
268 set_defaults();
269
270 guint length = g_strv_length(paths);
271
272 GPtrArray *conf_files;
273
274 if (length != 0) {
275 conf_files = g_ptr_array_new_full(length, g_free);
276 for (int i = 0; paths[i]; i++)
277 g_ptr_array_add(conf_files, g_strdup(paths[i]));
278 } else {
279 // Use default locations (and search drop-ins)
280 conf_files = get_conf_files();
281 }
282
283 /* Load all conf files and drop-ins, least important first. */
284 int n_loaded_confs = 0;
285 g_ptr_array_foreach(conf_files, process_conf_file, &n_loaded_confs);
286
287 if (0 == n_loaded_confs)
288 LOG_M("No configuration file found, using defaults");
289
290 g_ptr_array_unref(conf_files);
291}
292
293void settings_free(struct settings *s)
294{
295 gradient_release(s->colors_low.highlight);
296 gradient_release(s->colors_norm.highlight);
297 gradient_release(s->colors_crit.highlight);
298
299 g_free(s->font);
300 g_free(s->format);
301 g_free(s->icons[0]);
302 g_free(s->icons[1]);
303 g_free(s->icons[2]);
304 g_free(s->title);
305 g_free(s->class);
306 g_free(s->monitor);
307 g_free(s->dmenu);
308 g_strfreev(s->dmenu_cmd);
309 g_free(s->browser);
310 g_strfreev(s->browser_cmd);
311 g_strfreev(s->icon_theme);
312 g_free(s->icon_path);
313
314 g_free(s->mouse_left_click);
315 g_free(s->mouse_middle_click);
316 g_free(s->mouse_right_click);
317
318 g_free(s->close_ks.str);
319 g_free(s->close_all_ks.str);
320 g_free(s->history_ks.str);
321 g_free(s->context_ks.str);
322}
Main event loop logic.
Logging subsystem and helpers.
Parser for settings and cmdline arguments.
static int is_drop_in(const struct dirent *dent)
Filter for scandir().
Definition settings.c:43
#define SYSCONFDIR
Since this gets defined by $DEFAULT_CPPFLAGS at compile time doxygen seems to miss the correct value.
Definition settings.c:28
static GPtrArray * get_conf_files(void)
Find all config files.
Definition settings.c:113
static GPtrArray * get_xdg_conf_basedirs(void)
Get all relevant config base directories.
Definition settings.c:60
Type definitions for settings.
Definition ini.h:28
struct position offset
Definition settings.h:189
FILE * fopen_verbose(const char *const path)
Open files verbosely.
Definition utils.c:458
bool is_readable_file(const char *const path)
Check if file is readable.
Definition utils.c:437
char * string_to_path(char *string)
Replace tilde and path-specific values with it's equivalents.
Definition utils.c:178
void add_paths_from_env(GPtrArray *arr, char *env_name, char *subdir, char *alternative)
Adds the contents of env_name with subdir to the array, interpreting the environment variable as a co...
Definition utils.c:472
String, time and other various helpers.
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
Definition utils.h:26