1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2005 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
31 #include <X11/Xresource.h>
40 /* This file doesn't need the Xt headers, so stub these types out... */
42 #define XtAppContext void*
43 #define XtIntervalId void*
44 #define XtPointer void*
48 /* Just in case there's something pathological about stat.h... */
50 # define S_IRUSR 00400
53 # define S_IWUSR 00200
56 # define S_IXUSR 00100
59 # define S_IXGRP 00010
62 # define S_IXOTH 00001
67 #include "resources.h"
70 extern char *progname;
71 extern char *progclass;
72 extern const char *blurb (void);
76 static void get_screenhacks (saver_preferences *p);
77 static char *format_command (const char *cmd, Bool wrap_p);
78 static void merge_system_screenhacks (saver_preferences *p,
79 screenhack **system_list, int count);
80 static void stop_the_insanity (saver_preferences *p);
84 chase_symlinks (const char *file)
91 # define PATH_MAX MAXPATHLEN
93 # define PATH_MAX 2048
97 if (realpath (file, buf))
100 sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
103 # endif /* HAVE_REALPATH */
109 i_am_a_nobody (uid_t uid)
113 p = getpwnam ("nobody");
114 if (! p) p = getpwnam ("noaccess");
115 if (! p) p = getpwnam ("daemon");
117 if (! p) /* There is no nobody? */
120 return (uid == p->pw_uid);
125 init_file_name (void)
127 static char *file = 0;
131 uid_t uid = getuid ();
132 struct passwd *p = getpwuid (uid);
134 if (i_am_a_nobody (uid))
135 /* If we're running as nobody, then use root's .xscreensaver file
136 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
137 to be different -- if we didn't do this, then xscreensaver-demo
138 would appear to have no effect when the luser is running as root.)
144 if (!p || !p->pw_name || !*p->pw_name)
146 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
150 else if (!p->pw_dir || !*p->pw_dir)
152 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
153 blurb(), (p->pw_name ? p->pw_name : "???"));
158 const char *home = p->pw_dir;
159 const char *name = ".xscreensaver";
160 file = (char *) malloc(strlen(home) + strlen(name) + 2);
162 if (!*home || home[strlen(home)-1] != '/')
176 init_file_tmp_name (void)
178 static char *file = 0;
181 const char *name = init_file_name();
182 const char *suffix = ".tmp";
184 char *n2 = chase_symlinks (name);
191 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
193 strcat(file, suffix);
206 get_byte_resource (char *name, char *class)
208 char *s = get_string_resource (name, class);
213 while (isspace(*s2)) s2++;
214 while (*s2 >= '0' && *s2 <= '9')
216 n = (n * 10) + (*s2 - '0');
219 while (isspace(*s2)) s2++;
220 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
221 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
222 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
226 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
232 if (*s2 == 'b' || *s2 == 'B') s2++;
233 while (isspace(*s2)) s2++;
241 static const char * const prefs[] = {
245 "lockVTs", /* not saved */
258 "helpURL", /* not saved */
259 "loadURL", /* not saved */
260 "newLoginCommand", /* not saved */
268 "captureStdout", /* not saved -- obsolete */
269 "ignoreUninstalledPrograms",
277 "chooseRandomImages",
291 "windowCreationTimeout",
294 "mitSaverExtension", /* not saved -- obsolete */
296 "GetViewPortIsFullOfLies",
299 "overlayTextBackground", /* not saved -- X resources only */
300 "overlayTextForeground", /* not saved -- X resources only */
301 "bourneShell", /* not saved -- X resources only */
309 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
311 for (s2 = s; *s2; s2++)
313 for (s2--; s2 >= s; s2--)
314 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
326 handle_entry (XrmDatabase *db, const char *key, const char *value,
327 const char *filename, int line)
330 for (i = 0; prefs[i]; i++)
331 if (*prefs[i] && !strcasecmp(key, prefs[i]))
333 char *val = strdup(value);
334 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
335 strcpy(spec, progclass);
337 strcat(spec, prefs[i]);
339 XrmPutStringResource (db, spec, val);
346 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
347 blurb(), filename, line, key);
353 parse_init_file (saver_preferences *p)
355 time_t write_date = 0;
356 const char *name = init_file_name();
365 if (stat(name, &st) != 0)
367 p->init_file_date = 0;
371 in = fopen(name, "r");
374 char *buf = (char *) malloc(1024 + strlen(name));
375 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
381 if (fstat (fileno(in), &st) == 0)
383 write_date = st.st_mtime;
387 char *buf = (char *) malloc(1024 + strlen(name));
388 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
394 buf = (char *) malloc(buf_size);
396 while (fgets (buf, buf_size-1, in))
403 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
404 buf[L-2] == '\\')) /* or line ended with backslash */
406 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
412 buf = (char *) realloc(buf, buf_size);
416 if (!fgets (buf + L, buf_size-L-1, in))
421 /* Now handle other backslash escapes. */
424 for (i = 0; buf[i]; i++)
429 case 'n': buf[i] = '\n'; break;
430 case 'r': buf[i] = '\r'; break;
431 case 't': buf[i] = '\t'; break;
432 default: buf[i] = buf[i+1]; break;
434 for (j = i+2; buf[j]; j++)
442 if (*key == '#' || *key == '!' || *key == ';' ||
443 *key == '\n' || *key == 0)
446 value = strchr (key, ':');
449 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
456 value = strip(value);
460 handle_entry (&p->db, key, value, name, line);
465 p->init_file_date = write_date;
471 init_file_changed_p (saver_preferences *p)
473 const char *name = init_file_name();
476 if (!name) return False;
478 if (stat(name, &st) != 0)
481 if (p->init_file_date == st.st_mtime)
492 tab_to (FILE *out, int from, int to)
495 int to_mod = (to / tab_width) * tab_width;
496 while (from < to_mod)
499 from = (((from / tab_width) + 1) * tab_width);
510 stab_to (char *out, int from, int to)
513 int to_mod = (to / tab_width) * tab_width;
514 while (from < to_mod)
517 from = (((from / tab_width) + 1) * tab_width);
528 string_columns (const char *string, int length, int start)
532 const char *end = string + length;
537 else if (*string == '\t')
538 col = (((col / tab_width) + 1) * tab_width);
548 write_entry (FILE *out, const char *key, const char *value)
550 char *v = strdup(value ? value : "");
554 Bool programs_p = (!strcmp(key, "programs"));
555 int tab = (programs_p ? 32 : 16);
558 fprintf(out, "%s:", key);
559 col = strlen(key) + 1;
561 if (strlen(key) > 14)
562 col = tab_to (out, col, 20);
568 nl = strchr(v2, '\n');
572 if (first && programs_p)
574 col = tab_to (out, col, 77);
575 fprintf (out, " \\\n");
583 col = tab_to (out, col, 75);
584 fprintf (out, " \\n\\\n");
589 col = tab_to (out, col, tab);
592 string_columns(v2, strlen (v2), col) + col > 75)
599 while (v2[end] == ' ' || v2[end] == '\t')
601 while (v2[end] != ' ' && v2[end] != '\t' &&
602 v2[end] != '\n' && v2[end] != 0)
604 if (string_columns (v2 + start, (end - start), col) >= 74)
606 col = tab_to (out, col, 75);
607 fprintf(out, " \\\n");
608 col = tab_to (out, 0, tab + 2);
609 while (v2[start] == ' ' || v2[start] == '\t')
613 col = string_columns (v2 + start, (end - start), col);
615 fputc(v2[start++], out);
620 fprintf (out, "%s", v2);
621 col += string_columns(v2, strlen (v2), col);
635 write_init_file (saver_preferences *p, const char *version_string,
639 const char *name = init_file_name();
640 const char *tmp_name = init_file_tmp_name();
641 char *n2 = chase_symlinks (name);
645 /* Kludge, since these aren't in the saver_preferences struct as strings...
649 Bool overlay_stderr_p;
657 /* Throttle the various timeouts to reasonable values before writing
659 stop_the_insanity (p);
663 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
666 out = fopen(tmp_name, "w");
669 char *buf = (char *) malloc(1024 + strlen(name));
670 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
676 /* Give the new .xscreensaver file the same permissions as the old one;
677 except ensure that it is readable and writable by owner, and not
678 executable. Extra hack: if we're running as root, make the file
679 be world-readable (so that the daemon, running as "nobody", will
680 still be able to read it.)
682 if (stat(name, &st) == 0)
684 mode_t mode = st.st_mode;
685 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
686 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
688 if (getuid() == (uid_t) 0) /* read by group/other */
689 mode |= S_IRGRP | S_IROTH;
691 if (fchmod (fileno(out), mode) != 0)
693 char *buf = (char *) malloc(1024 + strlen(name));
694 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
695 tmp_name, (unsigned int) mode);
702 /* Kludge, since these aren't in the saver_preferences struct... */
703 visual_name = get_string_resource ("visualID", "VisualID");
705 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
706 stderr_font = get_string_resource ("font", "Font");
711 char **hack_strings = (char **)
712 calloc (p->screenhacks_count, sizeof(char *));
714 for (j = 0; j < p->screenhacks_count; j++)
716 hack_strings[j] = format_hack (p->screenhacks[j], True);
717 i += strlen (hack_strings[j]);
721 ss = programs = (char *) malloc(i + 10);
723 for (j = 0; j < p->screenhacks_count; j++)
725 strcat (ss, hack_strings[j]);
726 free (hack_strings[j]);
735 struct passwd *pw = getpwuid (getuid ());
736 char *whoami = (pw && pw->pw_name && *pw->pw_name
739 time_t now = time ((time_t *) 0);
740 char *timestr = (char *) ctime (&now);
741 char *nl = (char *) strchr (timestr, '\n');
744 "# %s Preferences File\n"
745 "# Written by %s %s for %s on %s.\n"
746 "# http://www.jwz.org/xscreensaver/\n"
748 progclass, progname, version_string, whoami, timestr);
751 for (j = 0; prefs[j]; j++)
754 const char *pr = prefs[j];
755 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
769 # define CHECK(X) else if (!strcmp(pr, X))
771 CHECK("timeout") type = pref_time, t = p->timeout;
772 CHECK("cycle") type = pref_time, t = p->cycle;
773 CHECK("lock") type = pref_bool, b = p->lock_p;
774 # if 0 /* #### not ready yet */
775 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
777 CHECK("lockVTs") continue; /* don't save */
779 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
780 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
781 CHECK("visualID") type = pref_str, s = visual_name;
782 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
783 CHECK("verbose") type = pref_bool, b = p->verbose_p;
784 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
785 CHECK("splash") type = pref_bool, b = p->splash_p;
786 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
787 CHECK("quad") type = pref_bool, b = p->quad_p;
788 CHECK("demoCommand") type = pref_str, s = p->demo_command;
789 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
790 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
791 CHECK("helpURL") continue; /* don't save */
792 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
793 CHECK("loadURL") continue; /* don't save */
794 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
795 CHECK("newLoginCommand") continue; /* don't save */
796 CHECK("nice") type = pref_int, i = p->nice_inferior;
797 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
798 CHECK("fade") type = pref_bool, b = p->fade_p;
799 CHECK("unfade") type = pref_bool, b = p->unfade_p;
800 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
801 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
802 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
803 CHECK("captureStdout") continue; /* don't save */
804 CHECK("ignoreUninstalledPrograms")
805 type = pref_bool, b = p->ignore_uninstalled_p;
807 CHECK("font") type = pref_str, s = stderr_font;
809 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
810 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
811 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
812 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
814 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
815 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
816 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
817 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
819 CHECK("mode") type = pref_str,
820 s = (p->mode == ONE_HACK ? "one" :
821 p->mode == BLANK_ONLY ? "blank" :
822 p->mode == DONT_BLANK ? "off" :
823 p->mode == RANDOM_HACKS_SAME
826 CHECK("selected") type = pref_int, i = p->selected_hack;
828 CHECK("textMode") type = pref_str,
829 s = (p->tmode == TEXT_URL ? "url" :
830 p->tmode == TEXT_LITERAL ? "literal" :
831 p->tmode == TEXT_FILE ? "file" :
832 p->tmode == TEXT_PROGRAM ? "program" :
834 CHECK("textLiteral") type = pref_str, s = p->text_literal;
835 CHECK("textFile") type = pref_str, s = p->text_file;
836 CHECK("textProgram") type = pref_str, s = p->text_program;
837 CHECK("textURL") type = pref_str, s = p->text_url;
839 CHECK("programs") type = pref_str, s = programs;
840 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
841 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
842 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
843 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
844 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
845 CHECK("mitSaverExtension") continue; /* don't save */
846 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
847 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
848 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
849 b = p->getviewport_full_of_lies_p;
850 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
851 CHECK("overlayTextBackground") continue; /* don't save */
852 CHECK("overlayTextForeground") continue; /* don't save */
853 CHECK("bourneShell") continue;
862 sprintf(buf, "%d", i);
866 s = b ? "True" : "False";
870 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
881 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
887 if (i >= (1<<30) && i == ((i >> 30) << 30))
888 sprintf(buf, "%dG", i >> 30);
889 else if (i >= (1<<20) && i == ((i >> 20) << 20))
890 sprintf(buf, "%dM", i >> 20);
891 else if (i >= (1<<10) && i == ((i >> 10) << 10))
892 sprintf(buf, "%dK", i >> 10);
894 sprintf(buf, "%d", i);
903 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
906 write_entry (out, pr, s);
911 if (visual_name) free(visual_name);
912 if (stderr_font) free(stderr_font);
913 if (programs) free(programs);
915 if (fclose(out) == 0)
917 time_t write_date = 0;
919 if (stat(tmp_name, &st) == 0)
921 write_date = st.st_mtime;
925 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
926 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
933 if (rename (tmp_name, name) != 0)
935 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
936 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
937 blurb(), tmp_name, name);
945 p->init_file_date = write_date;
947 /* Since the .xscreensaver file is used for IPC, let's try and make
948 sure that the bits actually land on the disk right away. */
951 status = 0; /* wrote and renamed successfully! */
956 char *buf = (char *) malloc(1024 + strlen(name));
957 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
970 /* Parsing the resource database
974 free_screenhack (screenhack *hack)
976 if (hack->visual) free (hack->visual);
977 if (hack->name) free (hack->name);
978 free (hack->command);
979 memset (hack, 0, sizeof(*hack));
984 free_screenhack_list (screenhack **list, int count)
988 for (i = 0; i < count; i++)
990 free_screenhack (list[i]);
996 /* Populate `saver_preferences' with the contents of the resource database.
997 Note that this may be called multiple times -- it is re-run each time
998 the ~/.xscreensaver file is reloaded.
1000 This function can be very noisy, since it issues resource syntax errors
1004 load_init_file (saver_preferences *p)
1006 static Bool first_time = True;
1008 screenhack **system_default_screenhacks = 0;
1009 int system_default_screenhack_count = 0;
1013 /* Get the programs resource before the .xscreensaver file has been
1014 parsed and merged into the resource database for the first time:
1015 this is the value of *programs from the app-defaults file.
1016 Then clear it out so that it will be parsed again later, after
1017 the init file has been read.
1019 get_screenhacks (p);
1020 system_default_screenhacks = p->screenhacks;
1021 system_default_screenhack_count = p->screenhacks_count;
1023 p->screenhacks_count = 0;
1026 if (parse_init_file (p) != 0) /* file might have gone away */
1027 if (!first_time) return;
1031 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
1032 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
1033 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
1034 p->lock_p = get_boolean_resource ("lock", "Boolean");
1035 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
1036 p->fade_p = get_boolean_resource ("fade", "Boolean");
1037 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
1038 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
1039 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
1040 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
1041 p->nice_inferior = get_integer_resource ("nice", "Nice");
1042 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1043 p->splash_p = get_boolean_resource ("splash", "Boolean");
1044 p->quad_p = get_boolean_resource ("quad", "Boolean");
1045 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1046 p->ignore_uninstalled_p = get_boolean_resource ("ignoreUninstalledPrograms",
1049 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1050 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1051 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1052 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1053 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1054 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1055 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1056 p->pointer_hysteresis = get_integer_resource ("pointerHysteresis","Integer");
1057 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1060 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1061 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1062 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1063 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1065 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1066 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1067 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1068 p->image_directory = get_string_resource ("imageDirectory",
1071 p->text_literal = get_string_resource ("textLiteral", "TextLiteral");
1072 p->text_file = get_string_resource ("textFile", "TextFile");
1073 p->text_program = get_string_resource ("textProgram", "TextProgram");
1074 p->text_url = get_string_resource ("textURL", "TextURL");
1076 p->shell = get_string_resource ("bourneShell", "BourneShell");
1078 p->demo_command = get_string_resource("demoCommand", "URL");
1079 p->prefs_command = get_string_resource("prefsCommand", "URL");
1080 p->help_url = get_string_resource("helpURL", "URL");
1081 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1082 p->new_login_command = get_string_resource("newLoginCommand",
1085 /* If "*splash" is unset, default to true. */
1087 char *s = get_string_resource ("splash", "Boolean");
1094 /* If "*grabDesktopImages" is unset, default to true. */
1096 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1100 p->grab_desktop_p = True;
1103 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1104 #if 0 /* ignore this, it is evil. */
1105 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1108 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1110 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1112 p->getviewport_full_of_lies_p =
1113 get_boolean_resource ("GetViewPortIsFullOfLies", "Boolean");
1115 get_screenhacks (p); /* Parse the "programs" resource. */
1118 char *s = get_string_resource ("selected", "Integer");
1120 p->selected_hack = -1;
1122 p->selected_hack = get_integer_resource ("selected", "Integer");
1124 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1125 p->selected_hack = -1;
1129 char *s = get_string_resource ("mode", "Mode");
1130 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1131 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1132 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1133 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1134 else p->mode = RANDOM_HACKS;
1139 char *s = get_string_resource ("textMode", "TextMode");
1140 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1141 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1142 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1143 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1144 else p->tmode = TEXT_DATE;
1148 if (system_default_screenhack_count) /* note: first_time is also true */
1150 merge_system_screenhacks (p, system_default_screenhacks,
1151 system_default_screenhack_count);
1152 free_screenhack_list (system_default_screenhacks,
1153 system_default_screenhack_count);
1154 system_default_screenhacks = 0;
1155 system_default_screenhack_count = 0;
1161 p->verbose_p = True;
1162 p->timestamp_p = True;
1163 p->initial_delay = 0;
1166 /* Throttle the various timeouts to reasonable values after reading the
1168 stop_the_insanity (p);
1172 /* If there are any hacks in the system-wide defaults that are not in
1173 the ~/.xscreensaver file, add the new ones to the end of the list.
1174 This does *not* actually save the file.
1177 merge_system_screenhacks (saver_preferences *p,
1178 screenhack **system_list, int system_count)
1180 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1185 for (i = 0; i < system_count; i++)
1188 Bool matched_p = False;
1190 for (j = 0; j < p->screenhacks_count; j++)
1193 if (!system_list[i]->name)
1194 system_list[i]->name = make_hack_name (system_list[i]->command);
1196 name = p->screenhacks[j]->name;
1198 name = make_hack_name (p->screenhacks[j]->command);
1200 matched_p = !strcasecmp (name, system_list[i]->name);
1202 if (name != p->screenhacks[j]->name)
1211 /* We have an entry in the system-wide list that is not in the
1212 user's .xscreensaver file. Add it to the end.
1213 Note that p->screenhacks is a single malloc block, not a
1214 linked list, so we have to realloc it.
1216 screenhack *oh = system_list[i];
1217 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1219 if (made_space == 0)
1222 p->screenhacks = (screenhack **)
1223 realloc (p->screenhacks,
1224 (p->screenhacks_count + made_space + 1)
1225 * sizeof(screenhack));
1226 if (!p->screenhacks) abort();
1229 nh->enabled_p = oh->enabled_p;
1230 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1231 nh->name = oh->name ? strdup(oh->name) : 0;
1232 nh->command = oh->command ? strdup(oh->command) : 0;
1234 p->screenhacks[p->screenhacks_count++] = nh;
1235 p->screenhacks[p->screenhacks_count] = 0;
1239 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1240 (nh->name ? nh->name : make_hack_name (nh->command)));
1248 /* Parsing the programs resource.
1252 parse_screenhack (const char *line)
1254 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1257 h->enabled_p = True;
1259 while (isspace(*line)) line++; /* skip whitespace */
1260 if (*line == '-') /* handle "-" */
1262 h->enabled_p = False;
1264 while (isspace(*line)) line++; /* skip whitespace */
1267 s = line; /* handle "visual:" */
1268 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1274 h->visual = (char *) malloc (line-s+1);
1275 strncpy (h->visual, s, line-s);
1276 h->visual[line-s] = 0;
1277 if (*line == ':') line++; /* skip ":" */
1278 while (isspace(*line)) line++; /* skip whitespace */
1281 if (*line == '"') /* handle "name" */
1285 while (*line && *line != '"')
1287 h->name = (char *) malloc (line-s+1);
1288 strncpy (h->name, s, line-s);
1289 h->name[line-s] = 0;
1290 if (*line == '"') line++; /* skip "\"" */
1291 while (isspace(*line)) line++; /* skip whitespace */
1294 h->command = format_command (line, False); /* handle command */
1300 format_command (const char *cmd, Bool wrap_p)
1304 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1305 const char *in = cmd;
1309 /* shrink all whitespace to one space, for the benefit of the "demo"
1310 mode display. We only do this when we can easily tell that the
1311 whitespace is not significant (no shell metachars).
1315 case '\'': case '"': case '`': case '\\':
1316 /* Metachars are scary. Copy the rest of the line unchanged. */
1318 *out++ = *in++, col++;
1321 case ' ': case '\t':
1322 /* Squeeze all other whitespace down to one space. */
1323 while (*in == ' ' || *in == '\t')
1325 *out++ = ' ', col++;
1329 /* Copy other chars unchanged. */
1330 *out++ = *in++, col++;
1337 /* Strip trailing whitespace */
1338 while (out > cmd2 && isspace (out[-1]))
1345 /* Returns a new string describing the shell command.
1346 This may be just the name of the program, capitalized.
1347 It also may be something from the resource database (gotten
1348 by looking for "hacks.XYZ.name", where XYZ is the program.)
1351 make_hack_name (const char *shell_command)
1353 char *s = strdup (shell_command);
1357 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1364 s2 = strrchr (s, '/'); /* if pathname, take last component */
1372 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1375 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1376 s2 = get_string_resource (res_name, res_name);
1383 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1384 if (*s2 >= 'A' && *s2 <= 'Z')
1387 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1389 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1391 if (s[0] == 'G' && s[1] == 'l' &&
1392 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1400 format_hack (screenhack *hack, Bool wrap_p)
1407 char *def_name = make_hack_name (hack->command);
1409 /* Don't ever write out a name for a hack if it's the same as the default.
1411 if (hack->name && !strcmp (hack->name, def_name))
1418 size = (2 * (strlen(hack->command) +
1419 (hack->visual ? strlen(hack->visual) : 0) +
1420 (hack->name ? strlen(hack->name) : 0) +
1422 h2 = (char *) malloc (size);
1425 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1427 if (hack->visual && *hack->visual) /* write visual name */
1429 if (hack->enabled_p) *out++ = ' ';
1431 strcpy (out, hack->visual);
1432 out += strlen (hack->visual);
1438 col = string_columns (h2, strlen (h2), 0);
1440 if (hack->name && *hack->name) /* write pretty name */
1442 int L = (strlen (hack->name) + 2);
1444 out = stab_to (out, col, tab - L - 2);
1448 strcpy (out, hack->name);
1449 out += strlen (hack->name);
1453 col = string_columns (h2, strlen (h2), 0);
1454 if (wrap_p && col >= tab)
1455 out = stab_to (out, col, 77);
1459 if (out >= h2+size) abort();
1463 col = string_columns (h2, strlen (h2), 0);
1464 out = stab_to (out, col, tab); /* indent */
1466 if (out >= h2+size) abort();
1467 s = format_command (hack->command, wrap_p);
1478 get_screenhacks (saver_preferences *p)
1486 d = get_string_resource ("monoPrograms", "MonoPrograms");
1487 if (d && !*d) { free(d); d = 0; }
1489 d = get_string_resource ("colorPrograms", "ColorPrograms");
1490 if (d && !*d) { free(d); d = 0; }
1495 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1496 see the manual for details.\n", blurb());
1500 d = get_string_resource ("programs", "Programs");
1502 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1504 p->screenhacks_count = 0;
1512 /* Count up the number of newlines (which will be equal to or larger than
1513 one less than the number of hacks.)
1515 for (i = j = 0; d[i]; i++)
1520 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1522 /* Iterate over the lines in `d' (the string with newlines)
1523 and make new strings to stuff into the `screenhacks' array.
1525 p->screenhacks_count = 0;
1526 while (start < size)
1528 /* skip forward over whitespace. */
1529 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1532 /* skip forward to newline or end of string. */
1534 while (d[end] != 0 && d[end] != '\n')
1537 /* null terminate. */
1540 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1541 if (p->screenhacks_count >= i)
1549 if (p->screenhacks_count == 0)
1551 free (p->screenhacks);
1557 /* Make sure all the values in the preferences struct are sane.
1560 stop_the_insanity (saver_preferences *p)
1562 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1563 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1564 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1565 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1566 if (p->notice_events_timeout <= 0)
1567 p->notice_events_timeout = 10000; /* 10 secs */
1568 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1570 if (! p->fade_p) p->unfade_p = False;
1572 /* The DPMS settings may have the value 0.
1573 But if they are negative, or are a range less than 10 seconds,
1574 reset them to sensible defaults. (Since that must be a mistake.)
1576 if (p->dpms_standby != 0 &&
1577 p->dpms_standby < 10 * 1000)
1578 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1579 if (p->dpms_suspend != 0 &&
1580 p->dpms_suspend < 10 * 1000)
1581 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1582 if (p->dpms_off != 0 &&
1583 p->dpms_off < 10 * 1000)
1584 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1586 /* suspend may not be greater than off, unless off is 0.
1587 standby may not be greater than suspend, unless suspend is 0.
1589 if (p->dpms_off != 0 &&
1590 p->dpms_suspend > p->dpms_off)
1591 p->dpms_suspend = p->dpms_off;
1592 if (p->dpms_suspend != 0 &&
1593 p->dpms_standby > p->dpms_suspend)
1594 p->dpms_standby = p->dpms_suspend;
1597 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1598 p->dpms_suspend == 0 &&
1600 p->dpms_enabled_p = False;
1603 /* Set watchdog timeout to about half of the cycle timeout, but
1604 don't let it be faster than 1/2 minute or slower than 1 minute.
1606 p->watchdog_timeout = p->cycle * 0.6;
1607 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1608 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1610 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1611 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;