1 /* -------------------------------
2 * vim:tabstop=4:shiftwidth=4
3 * settings.c
4 * Sun, 12 Sep 2004 18:55:53 +0700
5 * -------------------------------
6 * settings parser\container
7 * -------------------------------*/
8
9 #include <X11/X.h>
10 #include <X11/Xlib.h>
11 #include <X11/Xutil.h>
12
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <libgen.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <limits.h>
20 #include <assert.h>
21
22 #include "config.h"
23
24 #include "common.h"
25 #include "settings.h"
26 #include "debug.h"
27 #include "layout.h"
28 #include "list.h"
29 #include "tray.h"
30
31 #include "xutils.h"
32 #include "wmh.h"
33
34 /* Here we keep our filthy settings */
35 struct Settings settings;
36 /* Initialize data */
init_default_settings()37 void init_default_settings()
38 {
39 settings.bg_color_str = "gray";
40 settings.tint_color_str = "white";
41 settings.scrollbars_highlight_color_str = "white";
42 settings.display_str = NULL;
43 #ifdef DEBUG
44 settings.log_level = LOG_LEVEL_ERR;
45 #endif
46 settings.geometry_str = NULL;
47 settings.max_geometry_str = "0x0";
48 settings.icon_size = FALLBACK_ICON_SIZE;
49 settings.slot_size = -1;
50 settings.deco_flags = DECO_NONE;
51 settings.max_tray_dims.x = 0;
52 settings.max_tray_dims.y = 0;
53 settings.parent_bg = 0;
54 settings.shrink_back_mode = 1;
55 settings.sticky = 1;
56 settings.skip_taskbar = 1;
57 settings.transparent = 0;
58 settings.vertical = 0;
59 settings.grow_gravity = GRAV_N | GRAV_W;
60 settings.icon_gravity = GRAV_N | GRAV_W;
61 settings.wnd_type = _NET_WM_WINDOW_TYPE_DOCK;
62 settings.wnd_layer = NULL;
63 settings.wnd_name = PROGNAME;
64 settings.xsync = 0;
65 settings.need_help = 0;
66 settings.config_fname = NULL;
67 settings.full_pmt_search = 1;
68 settings.min_space_policy = 0;
69 settings.pixmap_bg = 0;
70 settings.bg_pmap_path = NULL;
71 settings.tint_level = 0;
72 settings.fuzzy_edges = 0;
73 settings.dockapp_mode = DOCKAPP_NONE;
74 settings.scrollbars_size = -1;
75 settings.scrollbars_mode = SB_MODE_NONE;
76 settings.scrollbars_inc = -1;
77 settings.wm_strut_mode = WM_STRUT_AUTO;
78 settings.kludge_flags = 0;
79 settings.remote_click_name = NULL;
80 settings.remote_click_btn = REMOTE_CLICK_BTN_DEFAULT;
81 settings.remote_click_cnt = REMOTE_CLICK_CNT_DEFAULT;
82 settings.remote_click_pos.x = REMOTE_CLICK_POS_DEFAULT;
83 settings.remote_click_pos.y = REMOTE_CLICK_POS_DEFAULT;
84 #ifdef DELAY_EMBEDDING_CONFIRMATION
85 settings.confirmation_delay = 3;
86 #endif
87 }
88
89 /* ******* general parsing utils ********* */
90
91 #define PARSING_ERROR(msg,str) if (!silent) LOG_ERROR(("Parsing error: " msg ", \"%s\" found\n", str));
92
93 /* Parse highlight color */
parse_scrollbars_highlight_color(char * str,char *** tgt,int silent)94 int parse_scrollbars_highlight_color(char *str, char ***tgt, int silent)
95 {
96 if (!strcasecmp(str, "disable"))
97 **tgt = NULL;
98 else {
99 **tgt = strdup(str);
100 if (**tgt == NULL) DIE_OOM(("Could not copy value from parameter\n"));
101 }
102 return SUCCESS;
103 }
104
105 /* Parse log level */
parse_log_level(char * str,int ** tgt,int silent)106 int parse_log_level(char *str, int **tgt, int silent)
107 {
108 if (!strcmp(str, "err"))
109 **tgt = LOG_LEVEL_ERR;
110 else if (!strcmp(str, "info"))
111 **tgt = LOG_LEVEL_INFO;
112 #ifdef DEBUG
113 else if (!strcmp(str, "trace"))
114 **tgt = LOG_LEVEL_TRACE;
115 #endif
116 else {
117 PARSING_ERROR("err, info, or trace expected", str);
118 return FAILURE;
119 }
120 return SUCCESS;
121 }
122
123 /* Parse dockapp mode */
parse_dockapp_mode(char * str,int ** tgt,int silent)124 int parse_dockapp_mode(char *str, int **tgt, int silent)
125 {
126 if (!strcmp(str, "none"))
127 **tgt = DOCKAPP_NONE;
128 else if (!strcmp(str, "simple"))
129 **tgt = DOCKAPP_SIMPLE;
130 else if (!strcmp(str, "wmaker"))
131 **tgt = DOCKAPP_WMAKER;
132 else {
133 PARSING_ERROR("none, simple, or wmaker expected", str);
134 return FAILURE;
135 }
136 return SUCCESS;
137 }
138
139 /* Parse gravity string ORing resulting value
140 * with current value of tgt */
parse_gravity(char * str,int ** tgt,int silent)141 int parse_gravity(char *str, int **tgt, int silent)
142 {
143 int i, r = 0, s;
144 if (str == NULL) goto fail;
145 s = strlen(str);
146 if (s > 2) goto fail;
147 for (i = 0; i < s; i++)
148 switch (tolower(str[i])) {
149 case 'n': r |= GRAV_N; break;
150 case 's': r |= GRAV_S; break;
151 case 'w': r |= GRAV_W; break;
152 case 'e': r |= GRAV_E; break;
153 default: goto fail;
154 }
155 if ((r & GRAV_N && r & GRAV_S) || (r & GRAV_E && r & GRAV_W))
156 goto fail;
157 **tgt = r;
158 return SUCCESS;
159 fail:
160 PARSING_ERROR("gravity expected", str);
161 return FAILURE;
162 }
163
164 /* Parse integer string storing resulting value in tgt */
parse_int(char * str,int ** tgt,int silent)165 int parse_int(char *str, int **tgt, int silent)
166 {
167 int r;
168 char *tail;
169 r = strtol(str, &tail, 0);
170 if (*tail == 0) {
171 **tgt = r;
172 return SUCCESS;
173 } else {
174 PARSING_ERROR("integer expected", str);
175 return FAILURE;
176 }
177 }
178
179 /* Parse kludges mode */
parse_kludges(char * str,int ** tgt,int silent)180 int parse_kludges(char *str, int **tgt, int silent)
181 {
182 char *curtok = str, *rest = str;
183 do {
184 if ((rest = strchr(rest, ',')) != NULL) *(rest++) = 0;
185 if (!strcasecmp(curtok, "fix_window_pos"))
186 **tgt |= KLUDGE_FIX_WND_POS;
187 /* else if (!strcasecmp(curtok, "fix_window_size"))*/
188 /* **tgt = KLUDGE_FIX_WND_SIZE;*/
189 else if (!strcasecmp(curtok, "force_icons_size"))
190 **tgt |= KLUDGE_FORCE_ICONS_SIZE;
191 else if (!strcasecmp(curtok, "use_icons_hints"))
192 **tgt |= KLUDGE_USE_ICONS_HINTS;
193 else {
194 PARSING_ERROR("kludge flag expected", curtok);
195 return FAILURE;
196 }
197 curtok = rest;
198 } while (rest != NULL);
199 return SUCCESS;
200 }
201
202 /* Parse strut mode */
parse_strut_mode(char * str,int ** tgt,int silent)203 int parse_strut_mode(char *str, int **tgt, int silent)
204 {
205 if (!strcasecmp(str, "auto"))
206 **tgt = WM_STRUT_AUTO;
207 else if (!strcasecmp(str, "top"))
208 **tgt = WM_STRUT_TOP;
209 else if (!strcasecmp(str, "bottom"))
210 **tgt = WM_STRUT_BOT;
211 else if (!strcasecmp(str, "left"))
212 **tgt = WM_STRUT_LFT;
213 else if (!strcasecmp(str, "right"))
214 **tgt = WM_STRUT_RHT;
215 else if (!strcasecmp(str, "none"))
216 **tgt = WM_STRUT_NONE;
217 else {
218 PARSING_ERROR("one of top, bottom, left, right, or auto expected", str);
219 return FAILURE;
220 }
221 return SUCCESS;
222 }
223
224 /* Parse boolean string storing result in tgt*/
parse_bool(char * str,int ** tgt,int silent)225 int parse_bool(char *str, int **tgt, int silent)
226 {
227 if (!strcasecmp(str, "yes") || !strcasecmp(str, "on") || !strcasecmp(str, "true") || !strcasecmp(str, "1"))
228 **tgt = True;
229 else if (!strcasecmp(str, "no") || !strcasecmp(str, "off") || !strcasecmp(str, "false") || !strcasecmp(str, "0"))
230 **tgt = False;
231 else {
232 PARSING_ERROR("boolean expected", str);
233 return FAILURE;
234 }
235 return SUCCESS;
236 }
237
238 /* Backwards version of the boolean parser */
parse_bool_rev(char * str,int ** tgt,int silent)239 int parse_bool_rev(char *str, int **tgt, int silent)
240 {
241 if (parse_bool(str, tgt, silent)) {
242 **tgt = !**tgt;
243 return SUCCESS;
244 }
245 return FAILURE;
246 }
247
248 /* Parse window layer string storing result in tgt */
parse_wnd_layer(char * str,char *** tgt,int silent)249 int parse_wnd_layer(char *str, char ***tgt, int silent)
250 {
251 if (!strcasecmp(str, "top"))
252 **tgt = _NET_WM_STATE_ABOVE;
253 else if (!strcasecmp(str, "bottom"))
254 **tgt = _NET_WM_STATE_BELOW;
255 else if (!strcasecmp(str, "normal"))
256 **tgt = NULL;
257 else {
258 PARSING_ERROR("window layer expected", str);
259 return FAILURE;
260 }
261 return SUCCESS;
262 }
263
264 /* Parse window type string storing result in tgt */
parse_wnd_type(char * str,char *** tgt,int silent)265 int parse_wnd_type(char *str, char ***tgt, int silent)
266 {
267 if (!strcasecmp(str, "dock"))
268 **tgt = _NET_WM_WINDOW_TYPE_DOCK;
269 else if (!strcasecmp(str, "toolbar"))
270 **tgt = _NET_WM_WINDOW_TYPE_TOOLBAR;
271 else if (!strcasecmp(str, "utility"))
272 **tgt = _NET_WM_WINDOW_TYPE_UTILITY;
273 else if (!strcasecmp(str, "normal"))
274 **tgt = _NET_WM_WINDOW_TYPE_NORMAL;
275 else if (!strcasecmp(str, "desktop"))
276 **tgt = _NET_WM_WINDOW_TYPE_DESKTOP;
277 else {
278 PARSING_ERROR("window type expected", str);
279 return FAILURE;
280 }
281 return SUCCESS;
282 }
283
284 /* Just copy string from arg to *tgt */
parse_copystr(char * str,char *** tgt,int silent)285 int parse_copystr(char *str, char ***tgt, int silent)
286 {
287 /* Valgrind note: this memory will never
288 * be freed before stalonetray's exit. */
289 **tgt = strdup(str);
290 if (**tgt == NULL) DIE_OOM(("Could not copy value from parameter\n"));
291 return SUCCESS;
292 }
293
294 /* Parses window decoration specification */
parse_deco(char * str,int ** tgt,int silent)295 int parse_deco(char *str, int **tgt, int silent)
296 {
297 if (!strcasecmp(str, "none"))
298 **tgt = DECO_NONE;
299 else if (!strcasecmp(str, "all"))
300 **tgt = DECO_ALL;
301 else if (!strcasecmp(str, "border"))
302 **tgt = DECO_BORDER;
303 else if (!strcasecmp(str, "title"))
304 **tgt = DECO_TITLE;
305 else {
306 PARSING_ERROR("decoration specification expected", str);
307 return FAILURE;
308 }
309 return SUCCESS;
310 }
311
312 /* Parses window decoration specification */
parse_sb_mode(char * str,int ** tgt,int silent)313 int parse_sb_mode(char *str, int **tgt, int silent)
314 {
315 if (!strcasecmp(str, "none"))
316 **tgt = 0;
317 else if (!strcasecmp(str, "vertical"))
318 **tgt = SB_MODE_VERT;
319 else if (!strcasecmp(str, "horizontal"))
320 **tgt = SB_MODE_HORZ;
321 else if (!strcasecmp(str, "all"))
322 **tgt = SB_MODE_HORZ | SB_MODE_VERT;
323 else {
324 PARSING_ERROR("scrollbars specification expected", str);
325 return FAILURE;
326 }
327 return SUCCESS;
328 }
329
330 #if 0
331 /* Parses remote op specification */
332 int parse_remote(char *str, void **tgt, int silent)
333 {
334 #define NEXT_TOK(str, rest) do { \
335 (str) = (rest); \
336 if ((str) != NULL) { \
337 (rest) = strchr((str), ','); \
338 if ((rest) != NULL) *((rest)++)=0; \
339 } \
340 } while(0)
341 #define PARSE_INT(tgt, str, tail, def, msg) do { \
342 if (str == NULL || *(str) == '\0') { \
343 (tgt) = def; \
344 } else { \
345 (tgt) = strtol((str), &(tail), 0); \
346 if (*(tail) != '\0') { \
347 PARSING_ERROR(msg, (str)); \
348 return FAILURE; \
349 } \
350 } \
351 } while(0)
352 /* Handy names for parameters */
353 int *flag = (int *) tgt[0];
354 char **name = (char **) tgt[1];
355 int *btn = (int *) tgt[2];
356 struct Point *pos = (struct Point *) tgt[3];
357 /* Local variables */
358 char *rest = str, *tail;
359 if (str == NULL || strlen(str) == 0) return FAILURE;
360 *flag = 1;
361 NEXT_TOK(str, rest);
362 *name = strdup(str);
363 NEXT_TOK(str, rest);
364 PARSE_INT(*btn, str, tail, INT_MIN, "remote click: button number expected");
365 NEXT_TOK(str, rest);
366 PARSE_INT(pos->x, str, tail, INT_MIN, "remote click: x coordinate expected");
367 NEXT_TOK(str, rest);
368 PARSE_INT(pos->y, str, tail, INT_MIN, "remote click: y coordinate expected");
369 return SUCCESS;
370 #undef NEXT_TOK
371 #undef PARSE_INT
372 }
373 #endif
374
parse_remote_click_type(char * str,int ** tgt,int silent)375 int parse_remote_click_type(char *str, int **tgt, int silent)
376 {
377 if (!strcasecmp(str, "single"))
378 **tgt = 1;
379 else if (!strcasecmp(str, "double"))
380 **tgt = 2;
381 else {
382 PARSING_ERROR("click type can be single or double", str);
383 return FAILURE;
384 }
385 return SUCCESS;
386 }
387
parse_pos(char * str,void ** tgt,int silent)388 int parse_pos(char *str, void **tgt, int silent)
389 {
390 struct Point *pos = (struct Point *) tgt[0];
391 unsigned int dummy;
392 XParseGeometry(str, &pos->x, &pos->y, &dummy, &dummy);
393 return SUCCESS;
394 }
395
396 /************ CLI **************/
397
398 #define MAX_TARGETS 10
399
400 /* parameter parser function */
401 typedef int (*param_parser_t) (char *str, void *tgt[MAX_TARGETS], int silent);
402
403 struct Param {
404 char *short_name; /* Short command line parameter name */
405 char *long_name; /* Long command line parameter name */
406 char *rc_name; /* Parameter name for rc file */
407 void *target[MAX_TARGETS];/* Pointers to the values that are set by this parameter */
408 param_parser_t parser; /* Pointer to parsing function */
409 int pass; /* 0th pass parameters are parsed before rc file,
410 1st pass parameters are parsed after it */
411 int takes_arg; /* Wheather this parameter takes an argument */
412 int optional_arg; /* Wheather the argument is optional */
413 char *default_arg_val; /* Default value of the argument if none is given */
414 /* char *desc; */ /* TODO: Description */
415 };
416
417 struct Param params[] = {
418 {"-display", NULL, "display", {&settings.display_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
419 {NULL, "--log-level", "log_level", {&settings.log_level}, (param_parser_t) &parse_log_level, 1, 1, 0, NULL},
420 {"-bg", "--background", "background", {&settings.bg_color_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
421 {"-c", "--config", NULL, {&settings.config_fname}, (param_parser_t) &parse_copystr, 0, 1, 0, NULL},
422 #ifdef DELAY_EMBEDDING_CONFIRMATION
423 {NULL, "--confirmation-delay", "confirmation_delay", {&settings.confirmation_delay}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
424 #endif
425 {"-d", "--decorations", "decorations", {&settings.deco_flags}, (param_parser_t) &parse_deco, 1, 1, 1, "all"},
426 {NULL, "--dockapp-mode", "dockapp_mode", {&settings.dockapp_mode}, (param_parser_t) &parse_dockapp_mode, 1, 1, 1, "simple"},
427 {"-f", "--fuzzy-edges", "fuzzy_edges", {&settings.fuzzy_edges}, (param_parser_t) &parse_int, 1, 1, 1, "2"},
428 {"-geometry", "--geometry", "geometry", {&settings.geometry_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
429 {NULL, "--grow-gravity", "grow_gravity", {&settings.grow_gravity}, (param_parser_t) &parse_gravity, 1, 1, 0, NULL},
430 {NULL, "--icon-gravity", "icon_gravity", {&settings.icon_gravity}, (param_parser_t) &parse_gravity, 1, 1, 0, NULL},
431 { "-i", "--icon-size", "icon_size", {&settings.icon_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
432 {"-h", "--help", NULL, {&settings.need_help}, (param_parser_t) &parse_bool, 0, 0, 0, "true" },
433 {NULL, "--kludges", "kludges", {&settings.kludge_flags}, (param_parser_t) &parse_kludges, 1, 1, 0, NULL},
434 {NULL, "--max-geometry", "max_geometry", {&settings.max_geometry_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
435 {NULL, "--no-shrink", "no_shrink", {&settings.shrink_back_mode}, (param_parser_t) &parse_bool_rev, 1, 1, 1, "true"},
436 {"-p", "--parent-bg", "parent_bg", {&settings.parent_bg}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
437 #ifdef XPM_SUPPORTED
438 {NULL, "--pixmap-bg", "pixmap_bg", {&settings.bg_pmap_path}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
439 #endif
440 {"-r", "--remote-click-icon", NULL, {&settings.remote_click_name}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
441 {NULL, "--remote-click-button", NULL, {&settings.remote_click_btn}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
442 {NULL, "--remote-click-position", NULL, {&settings.remote_click_pos}, (param_parser_t) &parse_pos, 1, 1, 0, NULL},
443 {NULL, "--remote-click-type", NULL, {&settings.remote_click_cnt}, (param_parser_t) &parse_remote_click_type, 1, 1, 0, NULL},
444 {NULL, "--scrollbars", "scrollbars", {&settings.scrollbars_mode}, (param_parser_t) &parse_sb_mode, 1, 1, 0, NULL},
445 {NULL, "--scrollbars-highlight", "scrollbars_highlight", {&settings.scrollbars_highlight_color_str}, (param_parser_t) &parse_scrollbars_highlight_color, 1, 1, 0, NULL},
446 {NULL, "--scrollbars-step", "scrollbars_step", {&settings.scrollbars_inc}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
447 {NULL, "--scrollbars-size", "scrollbars_size", {&settings.scrollbars_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
448 {NULL, "--skip-taskbar", "skip_taskbar", {&settings.skip_taskbar}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
449 {"-s", "--slot-size", "slot_size", {&settings.slot_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
450 {NULL, "--sticky", "sticky", {&settings.sticky}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
451 {NULL, "--tint-color", "tint_color", {&settings.tint_color_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
452 {NULL, "--tint-level", "tint_level", {&settings.tint_level}, (param_parser_t) &parse_int, 1, 1, 0, NULL},
453 {"-t", "--transparent", "transparent", {&settings.transparent}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
454 {"-v", "--vertical", "vertical", {&settings.vertical}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
455 {NULL, "--window-layer", "window_layer", {&settings.wnd_layer}, (param_parser_t) &parse_wnd_layer, 1, 1, 0, NULL},
456 {NULL, "--window-name", "window_name", {&settings.wnd_name}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL},
457 {NULL, "--window-strut", "window_strut", {&settings.wm_strut_mode}, (param_parser_t) &parse_strut_mode, 1, 1, 0, NULL},
458 {NULL, "--window-type", "window_type", {&settings.wnd_type}, (param_parser_t) &parse_wnd_type, 1, 1, 0, NULL},
459 {NULL, "--xsync", "xsync", {&settings.xsync}, (param_parser_t) &parse_bool, 1, 1, 1, "true"},
460 {NULL, NULL, NULL, {NULL}}
461 };
462
usage(char * progname)463 void usage(char *progname)
464 {
465 printf( "\nstalonetray "VERSION" [ " FEATURE_LIST " ]\n");
466 printf( "\nUsage: %s [options...]\n", progname);
467 printf( "\n"
468 "For short options argument can be specified as -o value or -ovalue.\n"
469 "For long options argument can be specified as --option value or\n"
470 "--option=value. All flag-options have expilicit optional boolean \n"
471 "argument, which can be true (1, yes) or false (0, no).\n"
472 "\n"
473 "Possible options are:\n"
474 " -display <display> use X display <display>\n"
475 " -bg, --background <color> select background color (default: #777777)\n"
476 " -c, --config <filename> read configuration from <file>\n"
477 " (instead of default $HOME/.stalonetrayrc)\n"
478 " -d, --decorations <deco> set what part of window decorations are\n"
479 " visible; deco can be: none (default),\n"
480 " title, border, all\n"
481 " --dockapp-mode [<mode>] enable dockapp mode; mode can be none (default),\n"
482 " simple (default if no mode specified), or wmaker\n"
483 " -f, --fuzzy-edges [<level>] set edges fuzziness level from\n"
484 " 0 (disabled) to 3 (maximum); works with\n"
485 " tinting and/or pixmap background;\n"
486 " if not specified, level defaults to 2\n"
487 " [-]-geometry <geometry> set initial tray`s geometry (width and height\n"
488 " are defined in icon slots; offsets are defined\n"
489 " in pixels)\n"
490 " --grow-gravity <gravity> set tray`s grow gravity,\n"
491 " either to N, S, W, E, NW, NE, SW, or SE\n"
492 " --icon-gravity <gravity> icon positioning gravity (NW, NE, SW, SE)\n"
493 " -i, --icon-size <n> set basic icon size to <n>; default is 24\n"
494 " -h, --help show this message\n"
495 #ifdef DEBUG
496 " --log-level <level> set the level of output to either err\n"
497 " (default), info, or trace\n"
498 #else
499 " --log-level <level> set the level of output to either err\n"
500 " (default), or info\n"
501 #endif
502 " --kludges <list> enable specific kludges to work around\n"
503 " non-conforming WMs and/or stalonetray bugs;\n"
504 " argument is a comma-separated list of:\n"
505 " - fix_window_pos (fix window position),\n"
506 " - force_icons_size (ignore icon resizes),\n"
507 " - use_icons_hints (use icon size hints)\n"
508 " --max-geometry <geometry> set tray maximal width and height; 0 indicates\n"
509 " no limit in respective direction\n"
510 " --no-shrink do not shrink window back after icon removal\n"
511 " -p, --parent-bg use parent for background\n"
512 " --pixmap-bg <pixmap> use pixmap for tray`s window background\n"
513 " -r, --remote-click-icon <name> remote control (assumes an instance of\n"
514 " stalonetray is already an active tray on this\n"
515 " screen); sends click to icon which window's \n"
516 " name is <name>\n"
517 " --remote-click-button <n> defines mouse button for --remote-click-icon\n"
518 " --remote-click-position <x>x<y> defines position for --remote-click-icon\n"
519 " --remote-click-type <type> defines click type for --remote-click-icon;\n"
520 " type can be either single, or double\n"
521 " --scrollbars <mode> set scrollbar mode either to all, horizontal,\n"
522 " vertical, or none\n"
523 " --scrollbars-highlight <mode> set scrollbar highlighting mode which can\n"
524 " be either color spec (default color is red)\n"
525 " or disable\n"
526 " --scrollbars-step <n> set scrollbar step to n pixels\n"
527 " --scrollbars-size <n> set scrollbar size to n pixels\n"
528 " --slot-size <n> set icon slot size to n\n"
529 " --skip-taskbar hide tray`s window from the taskbar\n"
530 " --sticky make tray`s window sticky across multiple\n"
531 " desktops/pages\n"
532 " -t, --transparent enable root transparency\n"
533 " --tint-color <color> tint tray background with color (not used\n"
534 " with plain color background)\n"
535 " --tint-level <level> set tinting level from 0 to 255\n"
536 " -v, --vertical use vertical layout of icons (horizontal\n"
537 " layout is used by default)\n"
538 " --window-layer <layer> set tray`s window EWMH layer\n"
539 " either to bottom, normal, or top\n"
540 " --window-strut <mode> set window strut mode to either auto,\n"
541 " left, right, top, or bottom\n"
542 " --window-type <type> set tray`s window EWMH type to either\n"
543 " normal, dock, toolbar, utility, desktop\n"
544 " --xsync operate on X server synchronously (SLOW)\n"
545 "\n"
546 );
547 }
548
549 /* Parse command line parameters */
parse_cmdline(int argc,char ** argv,int pass)550 int parse_cmdline(int argc, char **argv, int pass)
551 {
552 struct Param *p, *match;
553 char *arg, *progname = argv[0];
554 while (--argc > 0) {
555 argv++;
556 match = NULL;
557 for (p = params; p->parser != NULL; p++) {
558 if (p->takes_arg) {
559 if (p->short_name != NULL && strstr(*argv, p->short_name) == *argv) {
560 if ((*argv)[strlen(p->short_name)] != '\0') /* accept arguments in the form -a5 */
561 arg = *argv + strlen(p->short_name);
562 else if (argc > 1 && argv[1][0] != '-') { /* accept arguments in the form -a 5,
563 do not accept values starting with '-' */
564 arg = *(++argv);
565 argc--;
566 } else if(!p->optional_arg) { /* argument is missing */
567 LOG_ERROR(("%s expects an argument\n", p->short_name));
568 break;
569 } else /* argument is optional, use default value */
570 arg = p->default_arg_val;
571 } else if (p->long_name != NULL && strstr(*argv, p->long_name) == *argv) {
572 if ((*argv)[strlen(p->long_name)] == '=') /* accept arguments in the form --abcd=5 */
573 arg = *argv + strlen(p->long_name) + 1;
574 else if ((*argv)[strlen(p->long_name)] == '\0') { /* accept arguments in the from ---abcd 5 */
575 if (argc > 1 && argv[1][0] != '-' ) { /* arguments cannot start with the dash */
576 arg = *(++argv);
577 argc--;
578 } else if (!p->optional_arg) { /*argument is missing */
579 LOG_ERROR(("%s expects an argument\n", p->long_name));
580 break;
581 } else /* argument is optional, use default value */
582 arg = p->default_arg_val;
583 } else continue; /* just in case when there can be both --abc and --abcd */
584 } else continue;
585 match = p;
586 break;
587 } else if (strcmp(*argv, p->short_name) == 0 || strcmp(*argv, p->long_name) == 0) {
588 match = p;
589 arg = p->default_arg_val;
590 break;
591 }
592 }
593 #define USAGE_AND_DIE() do { usage(progname); DIE(("Could not parse command line\n")); } while (0)
594 if (match == NULL) USAGE_AND_DIE();
595 if (match->pass != pass) continue;
596 if (arg == NULL) DIE_IE(("Argument cannot be NULL!\n"));
597 LOG_TRACE(("cmdline: pass %d, param \"%s\", arg \"%s\"\n", pass, match->long_name != NULL ? match->long_name : match->short_name, arg));
598 if (!match->parser(arg, match->target, match->optional_arg)) {
599 if (match->optional_arg) {
600 argc++; argv--;
601 assert(arg != match->default_arg_val);
602 match->parser(match->default_arg_val, match->target, False);
603 } else
604 USAGE_AND_DIE();
605 }
606 }
607 if (settings.need_help) {
608 usage(progname);
609 exit(0);
610 }
611 return SUCCESS;
612 }
613
614 /************ .stalonetrayrc ************/
615
616 /**************************************************************************************
617 * <line> ::= [<whitespaces>] [(<arg> <whitespaces>)* <arg>] [<whitespaces>] <comment>
618 * <arg> ::= "<arbitrary-text>"|<text-without-spaces-and-#>
619 * <comment> ::= # <arbitrary-text>
620 **************************************************************************************/
621
622 /* Does exactly what its name says */
623 #define SKIP_SPACES(p) { for (; *p != 0 && isspace((int) *p); p++); }
624 /* Break the line in argc, argv pair */
get_args(char * line,int * argc,char *** argv)625 int get_args(char *line, int *argc, char ***argv)
626 {
627 int q_flag = 0;
628 char *arg_start, *q_pos;
629 *argc = 0;
630 *argv = NULL;
631 /* 1. Strip leading spaces */
632 SKIP_SPACES(line);
633 if (0 == *line) { /* meaningless line */
634 return SUCCESS;
635 }
636 arg_start = line;
637 /* 2. Strip comments */
638 for (; 0 != *line; line++) {
639 q_flag = ('"' == *line) ? !q_flag : q_flag;
640 if ('#' == *line && !q_flag) {
641 *line = 0;
642 break;
643 }
644 }
645 if (q_flag) { /* disbalance of quotes */
646 LOG_ERROR(("Disbalance of quotes\n"));
647 return FAILURE;
648 }
649 if (arg_start == line) { /* meaningless line */
650 return SUCCESS;
651 }
652 line--;
653 /* 3. Strip trailing spaces */
654 for (; line != arg_start && isspace((int) *line); line--);
655 if (arg_start == line) { /* meaningless line */
656 return FAILURE;
657 }
658 *(line + 1) = 0; /* this _is_ really ok since isspace(0) != 0 */
659 line = arg_start;
660 /* 4. Extract arguments */
661 do {
662 (*argc)++;
663 /* Add space to store one more argument */
664 if (NULL == (*argv = realloc(*argv, *argc * sizeof(char *))))
665 DIE_OOM(("Could not allocate memory to parse parameters\n"));
666 if (*arg_start == '"') { /* 4.1. argument is quoted: find matching quote */
667 arg_start++;
668 (*argv)[*argc - 1] = arg_start;
669 if (NULL == (q_pos = strchr(arg_start, '"'))) {
670 free(*argv);
671 DIE_IE(("Quotes balance calculation failed\n"));
672 return FAILURE;
673 }
674 arg_start = q_pos;
675 } else { /* 4.2. whitespace-separated argument: find fist whitespace */
676 (*argv)[*argc - 1] = arg_start;
677 for (; 0 != *arg_start && !isspace((int) *arg_start); arg_start++);
678 }
679 if (*arg_start != 0) {
680 *arg_start = 0;
681 arg_start++;
682 SKIP_SPACES(arg_start);
683 }
684 } while(*arg_start != 0);
685 return SUCCESS;
686 }
687
688 #define READ_BUF_SZ 512
689 /* Parses rc file (path is either taken from settings.config_fname
690 * or ~/.stalonetrayrc is used) */
parse_rc()691 void parse_rc()
692 {
693 char *home_dir;
694 static char config_fname[PATH_MAX];
695 FILE *cfg;
696
697 char buf[READ_BUF_SZ + 1];
698 int lnum = 0;
699
700 int argc;
701 char **argv, *arg;
702
703 struct Param *p, *match;
704
705 /* 1. Setup file name */
706 if (settings.config_fname == NULL) {
707 if ((home_dir = getenv("HOME")) == NULL) {
708 LOG_ERROR(("You have no $HOME. I'm sorry for you.\n"));
709 return;
710 }
711 snprintf(config_fname, PATH_MAX-1, "%s/%s", home_dir, STALONETRAY_RC);
712 settings.config_fname = config_fname;
713 }
714
715 LOG_INFO(("using config file \"%s\"\n", settings.config_fname));
716
717 /* 2. Open file */
718 cfg = fopen(settings.config_fname, "r");
719 if (cfg == NULL) {
720 LOG_ERROR(("could not open %s (%s)\n", settings.config_fname, strerror(errno)));
721 return;
722 }
723
724 /* 3. Read the file line by line */
725 buf[READ_BUF_SZ] = 0;
726 while (!feof(cfg)) {
727 lnum++;
728 if (fgets(buf, READ_BUF_SZ, cfg) == NULL) {
729 if (ferror(cfg)) LOG_ERROR(("read error (%s)\n", strerror(errno)));
730 break;
731 }
732
733 if (!get_args(buf, &argc, &argv)) {
734 DIE(("Configuration file parse error at %s:%d: could not parse line\n", settings.config_fname, lnum));
735 }
736 if (!argc) continue; /* This is empty/comment-only line */
737
738 match = NULL;
739 for (p = params; p->parser != NULL; p++) {
740 if (p->rc_name != NULL && strcmp(argv[0], p->rc_name) == 0) {
741 if (argc - 1 > (p->takes_arg ? 1 : 0) || (!p->optional_arg && argc - 1 < 1))
742 DIE(("Configuration file parse error at %s:%d:"
743 "invalid number of args for \"%s\" (%s required)\n",
744 settings.config_fname,
745 lnum,
746 argv[0],
747 p->optional_arg ? "0/1" : "1" ));
748 match = p;
749 arg = (!p->takes_arg || (p->optional_arg && argc == 1)) ? p->default_arg_val : argv[1];
750 break;
751 }
752 }
753 if (!match) {
754 DIE(("Configuration file parse error at %s:%d: unrecognized rc file keyword \"%s\".\n", settings.config_fname, lnum, argv[0]));
755 }
756 assert(arg != NULL);
757 LOG_TRACE(("rc: param \"%s\", arg \"%s\"\n", match->rc_name, arg));
758 if (!match->parser(arg, match->target, False)) {
759 DIE(("Configuration file parse error at %s:%d: could not parse argument for \"%s\".\n", settings.config_fname, lnum, argv[0]));
760 }
761 free(argv);
762 }
763
764 fclose(cfg);
765 }
766
767 /* Interpret all settings that need an open display or other settings */
interpret_settings()768 void interpret_settings()
769 {
770 static int gravity_matrix[11] = {
771 ForgetGravity,
772 EastGravity,
773 WestGravity,
774 ForgetGravity,
775 SouthGravity,
776 SouthEastGravity,
777 SouthWestGravity,
778 ForgetGravity,
779 NorthGravity,
780 NorthEastGravity,
781 NorthWestGravity
782 };
783 int geom_flags;
784 int rc;
785 int dummy;
786 XWindowAttributes root_wa;
787 /* Sanitize icon size */
788 val_range(settings.icon_size, MIN_ICON_SIZE, INT_MAX);
789 if (settings.slot_size < settings.icon_size) settings.slot_size = settings.icon_size;
790 /* Sanitize scrollbar settings */
791 if (settings.scrollbars_mode != SB_MODE_NONE) {
792 val_range(settings.scrollbars_inc, settings.slot_size / 2, INT_MAX);
793 if (settings.scrollbars_size < 0) settings.scrollbars_size = settings.slot_size / 4;
794 }
795 /* Sanitize all gravity strings */
796 settings.icon_gravity |= ((settings.icon_gravity & GRAV_V) ? 0 : GRAV_N);
797 settings.icon_gravity |= ((settings.icon_gravity & GRAV_H) ? 0 : GRAV_W);
798 settings.win_gravity = gravity_matrix[settings.grow_gravity];
799 settings.bit_gravity = gravity_matrix[settings.icon_gravity];
800 /* Parse all background-related settings */
801 #ifdef XPM_SUPPORTED
802 settings.pixmap_bg = (settings.bg_pmap_path != NULL);
803 #endif
804 if (settings.pixmap_bg) {
805 settings.parent_bg = False;
806 settings.transparent = False;
807 }
808 if (settings.transparent)
809 settings.parent_bg = False;
810 /* Parse color-related settings */
811 if (!x11_parse_color(tray_data.dpy, settings.bg_color_str, &settings.bg_color))
812 DIE(("Could not parse background color \"%s\"\n", settings.bg_color_str));
813 if (settings.scrollbars_highlight_color_str != NULL)
814 {
815 if (!x11_parse_color(tray_data.dpy, settings.scrollbars_highlight_color_str, &settings.scrollbars_highlight_color))
816 {
817 DIE(("Could not parse scrollbars highlight color \"%s\"\n", settings.bg_color_str));
818 }
819 }
820 /* Sanitize tint level value */
821 val_range(settings.tint_level, 0, 255);
822 if (settings.tint_level) {
823 /* Parse tint color */
824 if (!x11_parse_color(tray_data.dpy, settings.tint_color_str, &settings.tint_color))
825 DIE(("Could not parse tint color \"%s\"\n", settings.tint_color_str));
826 }
827 /* Sanitize edges fuzziness */
828 val_range(settings.fuzzy_edges, 0, 3);
829 /* Get dimensions of root window */
830 if (!XGetWindowAttributes(tray_data.dpy, DefaultRootWindow(tray_data.dpy), &root_wa))
831 DIE(("Could not get root window dimensions.\n"));
832 tray_data.root_wnd.x = 0;
833 tray_data.root_wnd.y = 0;
834 tray_data.root_wnd.width = root_wa.width;
835 tray_data.root_wnd.height = root_wa.height;
836 /* Parse geometry */
837 # define DEFAULT_GEOMETRY "1x1+0+0"
838 tray_data.xsh.flags = PResizeInc | PBaseSize;
839 tray_data.xsh.x = 0;
840 tray_data.xsh.y = 0;
841 tray_data.xsh.width_inc = settings.slot_size;
842 tray_data.xsh.height_inc = settings.slot_size;
843 tray_data.xsh.base_width = 0;
844 tray_data.xsh.base_height = 0;
845 tray_calc_window_size(0, 0, &tray_data.xsh.base_width, &tray_data.xsh.base_height);
846 geom_flags = XWMGeometry(tray_data.dpy, DefaultScreen(tray_data.dpy),
847 settings.geometry_str, DEFAULT_GEOMETRY, 0,
848 &tray_data.xsh, &tray_data.xsh.x, &tray_data.xsh.y,
849 &tray_data.xsh.width, &tray_data.xsh.height, &tray_data.xsh.win_gravity);
850 tray_data.xsh.win_gravity = settings.win_gravity;
851 tray_data.xsh.min_width = tray_data.xsh.width;
852 tray_data.xsh.min_height = tray_data.xsh.height;
853 tray_data.xsh.max_width = tray_data.xsh.width;
854 tray_data.xsh.min_height = tray_data.xsh.height;
855 tray_data.xsh.flags = PResizeInc | PBaseSize | PMinSize | PMaxSize | PWinGravity;
856 tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,
857 &settings.orig_tray_dims.x, &settings.orig_tray_dims.y);
858 /* Dockapp mode */
859 if (settings.dockapp_mode == DOCKAPP_WMAKER)
860 tray_data.xsh.flags |= USPosition;
861 else {
862 if (geom_flags & (XValue | YValue)) tray_data.xsh.flags |= USPosition; else tray_data.xsh.flags |= PPosition;
863 if (geom_flags & (WidthValue | HeightValue)) tray_data.xsh.flags |= USSize; else tray_data.xsh.flags |= PSize;
864 }
865 LOG_TRACE(("final geometry: %dx%d at (%d,%d)\n",
866 tray_data.xsh.width, tray_data.xsh.height,
867 tray_data.xsh.x, tray_data.xsh.y));
868 if ((geom_flags & XNegative) && (geom_flags & YNegative))
869 settings.geom_gravity = SouthEastGravity;
870 else if (geom_flags & YNegative)
871 settings.geom_gravity = SouthWestGravity;
872 else if (geom_flags & XNegative)
873 settings.geom_gravity = NorthEastGravity;
874 else
875 settings.geom_gravity = NorthWestGravity;
876 /* Set tray maximal width/height */
877 geom_flags = XParseGeometry(settings.max_geometry_str,
878 &dummy, &dummy,
879 (unsigned int *) &settings.max_tray_dims.x,
880 (unsigned int *) &settings.max_tray_dims.y);
881 LOG_TRACE(("max geometry from max_geometry_str: %dx%d\n",
882 settings.max_tray_dims.x,
883 settings.max_tray_dims.y));
884 if (!settings.max_tray_dims.x)
885 settings.max_tray_dims.x = root_wa.width;
886 else {
887 settings.max_tray_dims.x *= settings.slot_size;
888 val_range(settings.max_tray_dims.x, settings.orig_tray_dims.x, INT_MAX);
889 }
890 if (!settings.max_tray_dims.y)
891 settings.max_tray_dims.y = root_wa.height;
892 else {
893 settings.max_tray_dims.y *= settings.slot_size;
894 val_range(settings.max_tray_dims.y, settings.orig_tray_dims.y, INT_MAX);
895 }
896 LOG_TRACE(("max geometry after normalization: %dx%d\n",
897 settings.max_tray_dims.x,
898 settings.max_tray_dims.y));
899 /* XXX: this assumes certain degree of symmetry and in some point
900 * in the future this may not be the case... */
901 tray_calc_window_size(0, 0, &tray_data.scrollbars_data.scroll_base.x, &tray_data.scrollbars_data.scroll_base.y);
902 tray_data.scrollbars_data.scroll_base.x /= 2;
903 tray_data.scrollbars_data.scroll_base.y /= 2;
904 }
905
906 /************** "main" ***********/
read_settings(int argc,char ** argv)907 int read_settings(int argc, char **argv)
908 {
909 init_default_settings();
910 /* Parse 0th pass command line args */
911 parse_cmdline(argc, argv, 0);
912 /* Parse configuration files */
913 parse_rc();
914 /* Parse 1st pass command line args */
915 parse_cmdline(argc, argv, 1);
916 /* Display some settings */
917 LOG_TRACE(("stalonetray "VERSION" [ " FEATURE_LIST " ]\n"));
918 LOG_TRACE(("bg_color_str = \"%s\"\n", settings.bg_color_str));
919 LOG_TRACE(("config_fname = \"%s\"\n", settings.config_fname));
920 LOG_TRACE(("deco_flags = 0x%x\n", settings.deco_flags));
921 LOG_TRACE(("display_str = \"%s\"\n", settings.display_str));
922 LOG_TRACE(("dockapp_mode = %d\n", settings.dockapp_mode));
923 LOG_TRACE(("full_pmt_search = %d\n", settings.full_pmt_search));
924 LOG_TRACE(("geometry_str = \"%s\"\n", settings.geometry_str));
925 LOG_TRACE(("grow_gravity = 0x%x\n", settings.grow_gravity));
926 LOG_TRACE(("icon_gravity = 0x%x\n", settings.icon_gravity));
927 LOG_TRACE(("icon_size = %d\n", settings.icon_size));
928 LOG_TRACE(("log_level = %d\n", settings.log_level));
929 LOG_TRACE(("max_tray_dims.x = %d\n", settings.max_tray_dims.x));
930 LOG_TRACE(("max_tray_dims.y = %d\n", settings.max_tray_dims.y));
931 LOG_TRACE(("min_space_policy = %d\n", settings.min_space_policy));
932 LOG_TRACE(("need_help = %d\n", settings.need_help));
933 LOG_TRACE(("parent_bg = %d\n", settings.parent_bg));
934 LOG_TRACE(("scrollbars_highlight_color_str = \"%s\"\n", settings.scrollbars_highlight_color_str));
935 LOG_TRACE(("scrollbars_highlight_color.pixel = %ld\n", settings.scrollbars_highlight_color.pixel));
936 LOG_TRACE(("scrollbars_inc = %d\n", settings.scrollbars_inc));
937 LOG_TRACE(("scrollbars_mode = %d\n", settings.scrollbars_mode));
938 LOG_TRACE(("scrollbars_size = %d\n", settings.scrollbars_size));
939 LOG_TRACE(("shrink_back_mode = %d\n", settings.shrink_back_mode));
940 LOG_TRACE(("slot_size = %d\n", settings.slot_size));
941 LOG_TRACE(("vertical = %d\n", settings.vertical));
942 LOG_TRACE(("xsync = %d\n", settings.xsync));
943 return SUCCESS;
944 }
945
946