223ceab48bf0f2d66ec99fe92c10a0c85733c945
[xscreensaver] / hacks / xsublim.c
1 /****************************************************************************
2  *                                                                           *
3  * xsublim -- Submit.  Conform.  Obey.                                       *
4  *                                                                           *
5  * Copyright (c) 1999 - 2000 Greg Knauss (greg@eod.com)                      *
6  *                                                                           *
7  * Thanks to Jamie Zawinski, whose suggestions and advice made what was a    *
8  * boring little program into a less boring (and a _lot_ less little)        *
9  * program.                                                                  *
10  *                                                                           *
11  * Permission to use, copy, modify, distribute, and sell this software and   *
12  * its documentation for any purpose is hereby granted without fee, provided *
13  * that the above copyright notice appear in all copies and that both that   *
14  * copyright notice and this permission notice appear in supporting          *
15  * documentation.  No representations are made about the suitability of this *
16  * software for any purpose.  It is provided "as is" without express or      *
17  * implied warranty.                                                         *
18  *                                                                           *
19  * Stare into the subliminal for as long as you can...                       *
20  *                                                                           *
21  *****************************************************************************/
22
23
24 /* Warnings *******************************************************************
25
26         Please don't end this process with a SIGKILL.  If it's got the X server
27         grabbed when you do, you'll never get it back and it won't be my fault.
28 */
29
30
31 /* Arguments ******************************************************************
32
33         -font font           Font to use
34         -file filename       New-line delimited phrase file
35         -program executable  New-line delimited phrase-producing executable
36         -delayShow ms        Microsecs for display of each word
37         -delayWord ms        Microsecs for blank between words
38         -delayPhraseMin ms   Microsecs for min blank between phrases
39         -delayPhraseMax ms   Microsecs for max blank between phrases
40         -random              Show phrases in random order (Default)
41         -no-random           Show phrases in listed order
42         -screensaver         Wait for an active screensaver (Default)
43         -no-screensaver      Draw over active windows       
44         -outline             Draw a contrasting outline around words (Default)
45         -no-outline          Draw words without an outline
46         -center              Draw words in the center of the screen (Default)
47         -no-center           Draw words randomly around the screen
48 */
49
50
51 /* Changelog ******************************************************************
52
53         1.1.1  20000407  Added -program
54         1.1.0  19991221  Added -file
55         1.0.1  19990716  Assume that XGetImage()/XDestroyImage() don't leak,
56                           which they apparently don't.  I have no idea how I
57                           convinced myself that they did.  Huh.  (greg@eod.com)
58         1.0.0  19990716  Initial release
59 */
60
61
62 /* Defines *******************************************************************/
63 #define XSUBLIM_NAME               "XSublim"
64 #define XSUBLIM_TEXT_COUNT         1000
65 #define XSUBLIM_TEXT_LENGTH        128
66 #define XSUBLIM_TEXT_OUTLINE       1
67 #define XSUBLIM_PROGRAM_SIZE       1024*10
68 #define XSUBLIM_ARG_DELAYSHOW      "delayShow"
69 #define XSUBLIM_ARG_DELAYWORD      "delayWord"
70 #define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
71 #define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
72 #define XSUBLIM_ARG_RANDOM         "random"
73 #define XSUBLIM_ARG_FILE           "file"
74 #define XSUBLIM_ARG_PROGRAM        "program"
75 #define XSUBLIM_ARG_SCREENSAVER    "screensaver"
76 #define XSUBLIM_ARG_OUTLINE        "outline"
77 #define XSUBLIM_ARG_CENTER         "center"
78 #define XSUBLIM_ARG_FONT           "font"
79 #define XSUBLIM_ARG_PHRASES        "phrases"
80 #ifndef TRUE
81 #define FALSE 0
82 #define TRUE  1
83 #endif
84
85
86 /* Includes ******************************************************************/
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <signal.h>
91 #include <sys/stat.h>
92 #include <X11/Intrinsic.h>
93 #include <X11/IntrinsicP.h>
94 #include <X11/CoreP.h>
95 #include <X11/Shell.h>
96 #include <X11/StringDefs.h>
97 #include <X11/Xutil.h>
98 #include <X11/keysym.h>
99 #include <X11/Xatom.h>
100 #include <X11/Xlib.h>
101 #include <X11/Xos.h>
102 #include <X11/Xproto.h>
103 #if defined(__sgi)
104 #include <X11/SGIScheme.h>
105 #endif
106
107 #include "usleep.h"
108 #include "yarandom.h"
109 #include "resources.h"
110
111
112 /* Globals *******************************************************************/
113 char*        progname;
114 XtAppContext app;
115 XrmDatabase  db;
116 char*        progclass = XSUBLIM_NAME;
117 char*        defaults[] =
118 {
119         ".background:                     #000000",
120         ".foreground:                     #FFFFFF",
121         "*" XSUBLIM_ARG_PHRASES ":"
122          "Submit.\\n"
123          "Conform.\\n"
124          "Obey.\\n"
125          "OBEY. OBEY. OBEY.\\n"
126          "Consume.\\n"
127          "Be silent.\\n"
128          "Fear.\\n"
129          "Waste.\\n"
130          "Money.\\n"
131          "Watch TV.\\n"
132          "Hate yourself.\\n"
133          "Buy needlessly.\\n"
134          "Despair quietly.\\n"
135          "God hates you.\\n"
136          "You are being watched.\\n"
137          "You will be punished.\\n"
138          "You serve no purpose.\\n"
139          "Your contributions are ignored.\\n"
140          "They are laughing at you.\\n"
141          "They lied to you.\\n"
142          "They read your mail.\\n"
143          "They know.\\n"
144          "Surrender.\\n"
145          "You will fail.\\n"
146          "Never question.\\n"
147          "You are a prisoner.\\n"
148          "You are helpless.\\n"
149          "You are diseased.\\n"
150          "Fear the unknown.\\n"
151          "Happiness follows obedience.\\n"
152          "Ignorance is strength.\\n"
153          "War is peace.\\n"
154          "Freedom is slavery.\\n"
155          "Abandon all hope.\\n"
156          "You will be assimilated.\\n"
157          "Resistance is futile.\\n"
158          "Resistance is useless.\\n"
159          "Life is pain.\\n"
160          "No escape.\\n"
161          "What's that smell?\\n"
162          "All praise the company.\\n"
163          "Fnord.\\n",
164         "*" XSUBLIM_ARG_FONT ":           -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
165         "*" XSUBLIM_ARG_DELAYSHOW ":      40000",
166         "*" XSUBLIM_ARG_DELAYWORD ":      100000",
167         "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
168         "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
169         "*" XSUBLIM_ARG_RANDOM ":         true",
170         "*" XSUBLIM_ARG_SCREENSAVER ":    true",
171         "*" XSUBLIM_ARG_OUTLINE":         true",
172         "*" XSUBLIM_ARG_CENTER":          true",
173         NULL
174 };
175 XrmOptionDescRec options[] =
176 {
177         {"-" XSUBLIM_ARG_FONT,          "." XSUBLIM_ARG_FONT,
178          XrmoptionSepArg,0},
179         {"-" XSUBLIM_ARG_DELAYSHOW,     "." XSUBLIM_ARG_DELAYSHOW,
180          XrmoptionSepArg,0},
181         {"-" XSUBLIM_ARG_DELAYWORD,     "." XSUBLIM_ARG_DELAYWORD,
182          XrmoptionSepArg,0},
183         {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
184          XrmoptionSepArg,0},
185         {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
186          XrmoptionSepArg,0},
187         {"-" XSUBLIM_ARG_RANDOM,        "." XSUBLIM_ARG_RANDOM,
188          XrmoptionNoArg,"true"},
189         {"-no-" XSUBLIM_ARG_RANDOM,     "." XSUBLIM_ARG_RANDOM,
190          XrmoptionNoArg,"false"},
191         {"-" XSUBLIM_ARG_FILE,          "." XSUBLIM_ARG_FILE,
192          XrmoptionSepArg,0 },
193 #if !defined(VMS)
194         {"-" XSUBLIM_ARG_PROGRAM,       "." XSUBLIM_ARG_PROGRAM,
195          XrmoptionSepArg,0 },
196 #endif
197         {"-" XSUBLIM_ARG_SCREENSAVER,   "." XSUBLIM_ARG_SCREENSAVER,
198          XrmoptionNoArg,"true"},
199         {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
200          XrmoptionNoArg,"false"},
201         {"-" XSUBLIM_ARG_OUTLINE,       "." XSUBLIM_ARG_OUTLINE,
202          XrmoptionNoArg,"true"},
203         {"-no-" XSUBLIM_ARG_OUTLINE,    "." XSUBLIM_ARG_OUTLINE,
204          XrmoptionNoArg,"false"},
205         {"-" XSUBLIM_ARG_CENTER,        "." XSUBLIM_ARG_CENTER,
206          XrmoptionNoArg,"true"},
207         {"-no-" XSUBLIM_ARG_CENTER,     "." XSUBLIM_ARG_CENTER,
208          XrmoptionNoArg,"false"},
209         {NULL,                          NULL,
210          0,              0 }
211 };
212 static int Xsublim_Sig_Last;
213
214
215 /* Functions *****************************************************************/
216
217 /* Defer signals to protect the server grab ================================ */
218 void xsublim_Sig_Catch(int sig_Number)
219 {
220         /* BSD needs this reset each time, and it shouldn't hurt anything
221            else */
222         signal(sig_Number,xsublim_Sig_Catch);
223         Xsublim_Sig_Last = sig_Number;
224 }
225
226 /* Get the screensaver's window ============================================ */
227 static XErrorHandler Xsublim_Ss_Handler = NULL;
228 static int           Xsublim_Ss_Status;
229
230 /* This was all basically swiped from driver/remote.c and util/vroot.h */
231 static int xsublim_Ss_Handler(Display* handle_Display,
232             XErrorEvent* handle_Error)
233 {
234         if (handle_Error->error_code == BadWindow)
235         {
236                 Xsublim_Ss_Status = BadWindow;
237                 return 0;
238         }
239         if (Xsublim_Ss_Handler == NULL)
240         {
241                 fprintf(stderr,"%s: ",progname);
242                 abort();
243         }
244         return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
245 }
246 static Window xsublim_Ss_GetWindow(Display* ss_Display)
247 {
248         Window        win_Root;
249         Window        win_RootReturn;
250         Window        win_Parent;
251         Window*       win_Child;
252         Window        win_Win;
253         int           child_Count;
254         int           child_Index;
255         Atom          prop_Type;
256         int           prop_Format;
257         unsigned long prop_Count;
258         unsigned long prop_Bytes;
259         char*         prop_Value;
260         int           prop_Status;
261         static Atom   XA_SCREENSAVER_VERSION = -1;
262         static Atom   __SWM_VROOT;
263
264         /* Assume bad things */
265         win_Win = 0;
266         win_Child = NULL;
267
268         /* Find the atoms */
269         if (XA_SCREENSAVER_VERSION == -1)
270         {
271                 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
272                  "_SCREENSAVER_VERSION",FALSE);
273                 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
274         }
275
276         /* Find a screensaver window */
277         win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
278         if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
279          &win_Child,&child_Count) != FALSE)
280         {
281                 if (
282                  (win_Root == win_RootReturn) &&
283                  (win_Parent == 0) &&
284                  (win_Child != NULL) &&
285                  (child_Count > 0))
286                 {
287                         for (child_Index = 0;child_Index < child_Count;
288                          child_Index++)
289                         {
290                                 XSync(ss_Display,FALSE);
291                                 Xsublim_Ss_Status = 0;
292                                 Xsublim_Ss_Handler =
293                                  XSetErrorHandler(xsublim_Ss_Handler);
294                                 prop_Value = NULL;
295                                 prop_Status = XGetWindowProperty(ss_Display,
296                                  win_Child[child_Index],XA_SCREENSAVER_VERSION,
297                                  0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
298                                  &prop_Count,&prop_Bytes,
299                                  (unsigned char**)&prop_Value);
300                                 XSync(ss_Display,FALSE);
301                                 XSetErrorHandler(Xsublim_Ss_Handler);
302                                 if (prop_Value != NULL)
303                                 {
304                                         XFree(prop_Value);
305                                 }
306                                 if (Xsublim_Ss_Status == BadWindow)
307                                 {
308                                         prop_Status = BadWindow;
309                                 }
310                                 if ((prop_Status == Success) &&
311                                  (prop_Type != None))
312                                 {
313                                         /* See if it's a virtual root */
314                                         prop_Value = NULL;
315                                         prop_Status =
316                                          XGetWindowProperty(ss_Display,
317                                          win_Child[child_Index],__SWM_VROOT,0,
318                                          1,FALSE,XA_WINDOW,&prop_Type,
319                                          &prop_Format,&prop_Count,&prop_Bytes,
320                                          (unsigned char**)&prop_Value);
321                                         if (prop_Value != NULL)
322                                         {
323                                                 XFree(prop_Value);
324                                         }
325                                         if ((prop_Status == Success) &&
326                                          (prop_Type != None))
327                                         {
328                                                 win_Win =
329                                                  win_Child[child_Index];
330                                         }
331                                 }
332                         }
333                 }
334         }
335         if (win_Child != NULL)
336         {
337                 XFree(win_Child);
338         }
339         return win_Win;
340 }
341
342 /* Main ==================================================================== */
343 static XErrorHandler Xsublim_Sh_Handler = NULL;
344 static int           Xsublim_Sh_Status = 0;
345
346 static int xsublim_Sh_Handler(Display* handle_Display,
347             XErrorEvent* handle_Error)
348 {
349         if (handle_Error->error_code == BadMatch)
350         {
351                 Xsublim_Sh_Status = BadMatch;
352                 return 0;
353         }
354         if (Xsublim_Sh_Handler == NULL)
355         {
356                 fprintf(stderr,"%s: ",progname);
357                 abort();
358         }
359         return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
360 }
361 int main(int argc,char* argv[])
362 {
363         int               sig_Number;
364         int               sig_Signal[] =
365         {
366                 SIGHUP,
367                 SIGINT,
368                 SIGQUIT,
369                 SIGILL,
370                 SIGTRAP,
371 #if defined(SIGIOT)
372                 SIGIOT,
373 #endif
374                 SIGABRT,
375 #if defined(SIGEMT)
376                 SIGEMT,
377 #endif
378                 SIGFPE,
379                 SIGBUS,
380                 SIGSEGV,
381 #if defined(SIGSYS)
382                 SIGSYS,
383 #endif
384                 SIGTERM,
385 #if defined(SIGXCPU)
386                 SIGXCPU,
387 #endif
388 #if defined(SIGXFSZ)
389                 SIGXFSZ,
390 #endif
391 #if defined(SIGDANGER)
392                 SIGDANGER,
393 #endif
394                 -1
395         };
396         Widget            app_App;
397         Display*          disp_Display;
398         Window            win_Root;
399         XWindowAttributes attr_Win;
400         XGCValues         gc_ValFore;
401         XGCValues         gc_ValBack;
402         GC                gc_GcFore;
403         GC                gc_GcBack;
404         XFontStruct*      font_Font;
405         char*             font_List[] =
406         {
407                 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
408                 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
409                 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
410                 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
411                 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
412                 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
413                 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
414                 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
415                 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
416                 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
417                 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
418                 "fixed",
419                 NULL
420         };
421         int               font_Index;
422         int               text_Length;
423         int               text_X;
424         int               text_Y;
425         int               text_Width;
426         int               text_Height;
427         char*             text_List[XSUBLIM_TEXT_COUNT];
428         int               text_Used[XSUBLIM_TEXT_COUNT];
429         char              text_Text[XSUBLIM_TEXT_LENGTH+1];
430         char*             text_Phrase;
431         char*             text_Word;
432         int               text_Index;
433         int               text_Item;
434         int               text_Count;
435         struct
436         {
437                 int outline_X;
438                 int outline_Y;
439         }                 text_Outline[] =
440         {
441                 { -1,-1 },
442                 {  1,-1 },
443                 { -1, 1 },
444                 {  1, 1 },
445                 {  0, 0 }
446         };
447         int               text_OutlineIndex;
448         XImage*           image_Image = NULL;
449         int               image_X = 0;
450         int               image_Y = 0;
451         int               image_Width = 0;
452         int               image_Height = 0;
453         int               arg_Count;
454         int               arg_FlagCenter;
455         int               arg_FlagOutline;
456         int               arg_FlagScreensaver;
457         int               arg_FlagRandom;
458         int               arg_DelayShow;
459         int               arg_DelayWord;
460         int               arg_DelayPhraseMin;
461         int               arg_DelayPhraseMax;
462         char*             arg_Text;
463         char*             arg_Source;
464
465         /* Set-up ---------------------------------------------------------- */
466
467         /* Catch signals */
468         Xsublim_Sig_Last = -1;
469         for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
470         {
471                 signal(sig_Number,xsublim_Sig_Catch);
472         }
473
474         /* Randomize -- only need to do this here because this program
475            doesn't use the `screenhack.h' or `lockmore.h' APIs. */
476 # undef ya_rand_init
477         ya_rand_init (0);
478
479         /* Handle all the X nonsense */
480 #if defined(__sgi)
481         SgiUseSchemes("none");
482 #endif
483         for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
484         {
485                 ;
486         }
487         app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
488          defaults,0,0);
489
490         /* jwz */
491         if (argc > 1)
492           {
493             int x = 18;
494             int end = 78;
495             int i;
496             int count = (sizeof(options)/sizeof(*options))-1;
497             fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
498             fprintf (stderr, "Options include: ");
499             for (i = 0; i < count; i++)
500               {
501                 char *sw = options [i].option;
502                 Bool argp = (options [i].argKind == XrmoptionSepArg);
503                 int size = strlen (sw) + (argp ? 6 : 0) + 2;
504                 if (x + size >= end)
505                   {
506                     fprintf (stderr, "\n\t\t ");
507                     x = 18;
508                   }
509                 x += size;
510                 fprintf (stderr, "%s", sw);
511                 if (argp) fprintf (stderr, " <arg>");
512                 if (i != count-1) fprintf (stderr, ", ");
513               }
514             fprintf (stderr, ".\n");
515             exit (-1);
516           }
517
518         disp_Display = XtDisplay(app_App);
519         db = XtDatabase(disp_Display);
520         XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
521         win_Root = RootWindowOfScreen(XtScreen(app_App));
522         XtDestroyWidget(app_App);
523
524         /* Get the arguments */
525         arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
526         arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
527         arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
528          "Boolean");
529         arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
530         arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
531         arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
532         arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
533          "Integer");
534         arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
535          "Integer");
536         if (arg_DelayPhraseMax < arg_DelayPhraseMin)
537         {
538                 arg_DelayPhraseMax = arg_DelayPhraseMin;
539         }
540
541         /* Get the phrases */
542         text_Index = 0;
543         text_Item = 0;
544         text_Count = 0;
545         memset(text_Used,0,sizeof(text_Used));
546         arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
547         if (arg_Source != NULL)
548         {
549                 FILE*       file_Fs;
550                 struct stat file_Stat;
551
552                 file_Fs = fopen(arg_Source,"rb");
553                 if (file_Fs == NULL)
554                 {
555                         fprintf(stderr,"%s: Could not open '%s'\n",progname,
556                          arg_Source);
557                         exit(-1);
558                 }
559                 if (fstat(fileno(file_Fs),&file_Stat) != 0)
560                 {
561                         fprintf(stderr,"%s: Could not stat '%s'\n",progname,
562                          arg_Source);
563                         exit(-1);
564                 }
565                 arg_Text = calloc(1,file_Stat.st_size+1);
566                 if (arg_Text != NULL)
567                 {
568                         if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
569                         {
570                                 fprintf(stderr,"%s: Could not read '%s'\n",
571                                  progname,arg_Source);
572                                 exit(-1);
573                         }
574                 }
575                 fclose(file_Fs);
576         }
577         else
578         {
579                 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
580                  "Executable");
581                 if (arg_Source != NULL)
582                 {
583                         char* exe_Command = calloc(1,strlen(arg_Source)+10);
584                         FILE* exe_Fs;
585
586                         if (exe_Command == NULL)
587                         {
588                                 fprintf(stderr,
589                                  "%s: Could not allocate space for '%s'\n",
590                                  progname,arg_Source);
591                                 exit(-1);
592                         }
593                         sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
594
595                         exe_Fs = popen(exe_Command,"r");
596                         if (exe_Fs == NULL)
597                         {
598                                 fprintf(stderr,"%s: Could not run '%s'\n",
599                                  progname,arg_Source);
600                                 exit(-1);
601                         }
602                         arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
603                         if (arg_Text != NULL)
604                         {
605                                 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
606                                  exe_Fs) <= 0)
607                                 {
608                                         fprintf(stderr,
609                                          "%s: Could not read output of '%s'\n",
610                                          progname,arg_Source);
611                                         exit(-1);
612                                 }
613                                 if (
614                                  strstr(arg_Text,": not found") ||
615                                  strstr(arg_Text,": Not found") ||
616                                  strstr(arg_Text,": command not found") ||
617                                  strstr(arg_Text,": Command not found"))
618                                 {
619                                         fprintf(stderr,
620                                          "%s: Could not find '%s'\n",
621                                          progname,arg_Source);
622                                         exit(-1);
623                                 }
624                         }
625                         fclose(exe_Fs);
626                 }
627                 else
628                 {
629                         arg_Text =
630                          get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
631                         if (arg_Text != NULL)
632                         {
633                                 arg_Text = strdup(arg_Text);
634                         }
635                 }
636         }
637         if (arg_Text != NULL)
638         {
639                 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
640                  (text_Count < XSUBLIM_TEXT_COUNT))
641                 {
642                         arg_Text = NULL;
643                         text_List[text_Count] = text_Phrase;
644                         text_Count++;
645                 }
646                 text_List[text_Count] = NULL;
647         }
648         if (text_Count == 0)
649         {
650                 fprintf(stderr,"%s: No text to display\n",progname);
651                 exit(-1);
652         }
653
654         /* Load the font */
655         font_Font = XLoadQueryFont(disp_Display,
656          get_string_resource(XSUBLIM_ARG_FONT,"Font"));
657         font_Index = 0;
658         while ((font_Font == NULL) && (font_List[font_Index] != NULL))
659         {
660                 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
661                 font_Index++;
662         }
663         if (font_Font == NULL)
664         {
665                 fprintf(stderr,"%s: Couldn't load a font\n",progname);
666                 exit(-1);
667         }
668
669         /* Create the GCs */
670         XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
671         gc_ValFore.font = font_Font->fid;
672         gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
673          disp_Display,attr_Win.colormap);
674         gc_ValFore.background = get_pixel_resource("background","Background",
675          disp_Display,attr_Win.colormap);
676         gc_ValFore.subwindow_mode = IncludeInferiors;
677         gc_GcFore = XCreateGC(disp_Display,win_Root,
678          (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
679         gc_ValBack.font = font_Font->fid;
680         gc_ValBack.foreground = get_pixel_resource("background","Background",
681          disp_Display,attr_Win.colormap);
682         gc_ValBack.background = get_pixel_resource("foreground","Foreground",
683          disp_Display,attr_Win.colormap);
684         gc_ValBack.subwindow_mode = IncludeInferiors;
685         gc_GcBack = XCreateGC(disp_Display,win_Root,
686          (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
687
688         /* Loop ------------------------------------------------------------ */
689         while (Xsublim_Sig_Last == -1)
690         {
691                 /* Once-per-phrase stuff ----------------------------------- */
692
693                 /* If we're waiting for a screensaver... */
694                 if (arg_FlagScreensaver != FALSE)
695                 {
696                         /* Find the screensaver's window */
697                         win_Root = xsublim_Ss_GetWindow(disp_Display);
698                         if (win_Root == 0)
699                         {
700                                 usleep(30000000);
701                                 continue;
702                         }
703                 }
704
705                 /* Pick the next phrase */
706                 if (arg_FlagRandom != FALSE)
707                 {
708                         text_Item = random()%text_Count;
709                         text_Index = 0;
710                 }
711                 while (text_Used[text_Item] != FALSE)
712                 {
713                         text_Index++;
714                         text_Item++;
715                         if (text_Index == text_Count)
716                         {
717                                 text_Index = 0;
718                                 memset(text_Used,0,sizeof(text_Used));
719                         }
720                         if (text_List[text_Item] == NULL)
721                         {
722                                 text_Item = 0;
723                         }
724                 }
725                 text_Used[text_Item] = TRUE;
726                 strncpy(text_Text,text_List[text_Item],
727                  XSUBLIM_TEXT_LENGTH);
728                 text_Phrase = text_Text;
729
730                 /* Run through the phrase */
731                 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
732                  (Xsublim_Sig_Last == -1))
733                 {
734                         text_Phrase = NULL;
735
736                         /* Once-per-word stuff ----------------------------- */
737
738                         /* Find the text's position */
739                         XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
740                         text_Length = strlen(text_Word);
741                         text_Width = XTextWidth(font_Font,text_Word,
742                          text_Length)+XSUBLIM_TEXT_OUTLINE*2;
743                         text_Height = font_Font->ascent+font_Font->descent+1+
744                          XSUBLIM_TEXT_OUTLINE*2;
745                         if (arg_FlagCenter == FALSE)
746                         {
747                                 text_X = random()%(attr_Win.width-text_Width);
748                                 text_Y = random()%(attr_Win.height-
749                                  text_Height);
750                         }
751                         else
752                         {
753                                 text_X = (attr_Win.width/2)-(text_Width/2);
754                                 text_Y = (attr_Win.height/2)-(text_Height/2);
755                         }
756
757                         /* Find the image's position (and pad it out slightly,
758                            otherwise bits of letter get left behind -- are
759                            there boundry issues I don't know about?) */
760                         image_X = text_X-16;
761                         image_Y = text_Y;
762                         image_Width = text_Width+32;
763                         image_Height = text_Height;
764                         if (image_X < 0)
765                         {
766                                 image_X = 0;
767                         }
768                         if (image_Y < 0)
769                         {
770                                 image_Y = 0;
771                         }
772                         if (image_X+image_Width > attr_Win.width)
773                         {
774                                 image_Width = attr_Win.width-image_X;
775                         }
776                         if (image_Y+image_Height > attr_Win.height)
777                         {
778                                 image_Height = attr_Win.height-image_Y;
779                         }
780
781                         /* Influence people for our own ends --------------- */
782
783                         /* Grab the server -- we can't let anybody draw over
784                            us */
785                         XSync(disp_Display,FALSE);
786                         XGrabServer(disp_Display);
787                         XSync(disp_Display,FALSE);
788
789                         /* Set up an error handler that ignores BadMatches --
790                            since the screensaver can take its window away at
791                            any time, any call that uses it might choke */
792                         Xsublim_Sh_Status = 0;
793                         Xsublim_Sh_Handler =
794                          XSetErrorHandler(xsublim_Sh_Handler);
795
796                         /* Save the current background */
797                         image_Image = XGetImage(disp_Display,win_Root,image_X,
798                          image_Y,image_Width,image_Height,~0L,ZPixmap);
799
800                         /* If we've successfully saved the background... */
801                         if (image_Image != NULL)
802                         {
803                                 if (Xsublim_Sh_Status == 0)
804                                 {
805                                         /* Draw the outline */
806                                         if (arg_FlagOutline != FALSE)
807                                         {
808                                                 for (text_OutlineIndex = 0;
809                                                  text_Outline[
810                                                  text_OutlineIndex].outline_X
811                                                  != 0;text_OutlineIndex++)
812                                                 {
813                                                         /* Y'know, eight
814                                                            character tabs and
815                                                            descriptive variable
816                                                            names become
817                                                            annoying at some
818                                                            point... */
819                                                         XDrawString(
820                                                          disp_Display,
821                                                          win_Root,gc_GcBack,
822                                                          text_X+text_Outline[
823                                                          text_OutlineIndex].
824                                                          outline_X*
825                                                          XSUBLIM_TEXT_OUTLINE,
826                                                          text_Y+
827                                                          (font_Font->ascent)+
828                                                          text_Outline[
829                                                          text_OutlineIndex].
830                                                          outline_Y*
831                                                          XSUBLIM_TEXT_OUTLINE,
832                                                          text_Word,
833                                                          text_Length);
834                                                 }
835                                         }
836
837                                         /* Draw the word */
838                                         XDrawString(disp_Display,win_Root,
839                                          gc_GcFore,text_X,
840                                          text_Y+(font_Font->ascent),text_Word,
841                                          text_Length);
842                                 }
843                                 if (Xsublim_Sh_Status == 0)
844                                 {
845                                         /* Wait a bit */
846                                         XSync(disp_Display,FALSE);
847                                         if (Xsublim_Sig_Last == -1)
848                                         {
849                                                 usleep(arg_DelayShow);
850                                         }
851         
852                                         /* Restore the background */
853                                         XPutImage(disp_Display,win_Root,
854                                          gc_GcFore,image_Image,0,0,image_X,
855                                          image_Y,image_Width,image_Height);
856                                 }
857
858                                 /* Free the image */
859                                 XDestroyImage(image_Image);
860                         }
861
862                         /* Restore the error handler, ungrab the server */
863                         XSync(disp_Display,FALSE);
864                         XSetErrorHandler(Xsublim_Sh_Handler);
865                         XUngrabServer(disp_Display);
866                         XSync(disp_Display,FALSE);
867
868                         /* Pause between words */
869                         if (Xsublim_Sig_Last == -1)
870                         {
871                                 usleep(arg_DelayWord);
872                         }
873                 }
874
875                 /* Pause between phrases */
876                 if (Xsublim_Sig_Last == -1)
877                 {
878                         usleep(random()%(arg_DelayPhraseMax-
879                          arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
880                 }
881         }
882
883         /* Exit ------------------------------------------------------------ */
884         for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
885         {
886                 signal(sig_Number,SIG_DFL);
887         }
888         kill(getpid(),Xsublim_Sig_Last);
889
890         return 0;
891 }