http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[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 #include "vroot.h"
111
112
113 /* Globals *******************************************************************/
114 char*        progname;
115 char*        progclass = XSUBLIM_NAME;
116
117 # ifdef __GNUC__
118   __extension__   /* don't warn about "string length is greater than the
119                      length ISO C89 compilers are required to support"
120                      in the following string constant... */
121 # endif
122
123 static char*        defaults[] =
124 {
125         ".background:                     #000000",
126         ".foreground:                     #FFFFFF",
127         "*" XSUBLIM_ARG_PHRASES ":"
128          "Submit.\\n"
129          "Conform.\\n"
130          "Obey.\\n"
131          "OBEY. OBEY. OBEY.\\n"
132          "Consume.\\n"
133          "Be silent.\\n"
134          "Fear.\\n"
135          "Waste.\\n"
136          "Money.\\n"
137          "Watch TV.\\n"
138          "Hate yourself.\\n"
139          "Buy needlessly.\\n"
140          "Despair quietly.\\n"
141          "God hates you.\\n"
142          "You are being watched.\\n"
143          "You will be punished.\\n"
144          "You serve no purpose.\\n"
145          "Your contributions are ignored.\\n"
146          "They are laughing at you.\\n"
147          "They lied to you.\\n"
148          "They read your mail.\\n"
149          "They know.\\n"
150          "Surrender.\\n"
151          "You will fail.\\n"
152          "Never question.\\n"
153          "You are a prisoner.\\n"
154          "You are helpless.\\n"
155          "You are diseased.\\n"
156          "Fear the unknown.\\n"
157          "Happiness follows obedience.\\n"
158          "Ignorance is strength.\\n"
159          "War is peace.\\n"
160          "Freedom is slavery.\\n"
161          "Abandon all hope.\\n"
162          "You will be assimilated.\\n"
163          "Resistance is futile.\\n"
164          "Resistance is useless.\\n"
165          "Life is pain.\\n"
166          "No escape.\\n"
167          "What's that smell?\\n"
168          "All praise the company.\\n"
169          "Fnord.\\n",
170         "*" XSUBLIM_ARG_FONT ":           -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
171         "*" XSUBLIM_ARG_DELAYSHOW ":      40000",
172         "*" XSUBLIM_ARG_DELAYWORD ":      100000",
173         "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
174         "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
175         "*" XSUBLIM_ARG_RANDOM ":         true",
176         "*" XSUBLIM_ARG_SCREENSAVER ":    true",
177         "*" XSUBLIM_ARG_OUTLINE":         true",
178         "*" XSUBLIM_ARG_CENTER":          true",
179         NULL
180 };
181
182 static XrmOptionDescRec options[] =
183 {
184         {"-" XSUBLIM_ARG_FONT,          "." XSUBLIM_ARG_FONT,
185          XrmoptionSepArg,0},
186         {"-" XSUBLIM_ARG_DELAYSHOW,     "." XSUBLIM_ARG_DELAYSHOW,
187          XrmoptionSepArg,0},
188         {"-" XSUBLIM_ARG_DELAYWORD,     "." XSUBLIM_ARG_DELAYWORD,
189          XrmoptionSepArg,0},
190         {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
191          XrmoptionSepArg,0},
192         {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
193          XrmoptionSepArg,0},
194         {"-" XSUBLIM_ARG_RANDOM,        "." XSUBLIM_ARG_RANDOM,
195          XrmoptionNoArg,"true"},
196         {"-no-" XSUBLIM_ARG_RANDOM,     "." XSUBLIM_ARG_RANDOM,
197          XrmoptionNoArg,"false"},
198         {"-" XSUBLIM_ARG_FILE,          "." XSUBLIM_ARG_FILE,
199          XrmoptionSepArg,0 },
200 #if !defined(VMS)
201         {"-" XSUBLIM_ARG_PROGRAM,       "." XSUBLIM_ARG_PROGRAM,
202          XrmoptionSepArg,0 },
203 #endif
204         {"-" XSUBLIM_ARG_SCREENSAVER,   "." XSUBLIM_ARG_SCREENSAVER,
205          XrmoptionNoArg,"true"},
206         {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
207          XrmoptionNoArg,"false"},
208         {"-" XSUBLIM_ARG_OUTLINE,       "." XSUBLIM_ARG_OUTLINE,
209          XrmoptionNoArg,"true"},
210         {"-no-" XSUBLIM_ARG_OUTLINE,    "." XSUBLIM_ARG_OUTLINE,
211          XrmoptionNoArg,"false"},
212         {"-" XSUBLIM_ARG_CENTER,        "." XSUBLIM_ARG_CENTER,
213          XrmoptionNoArg,"true"},
214         {"-no-" XSUBLIM_ARG_CENTER,     "." XSUBLIM_ARG_CENTER,
215          XrmoptionNoArg,"false"},
216         {NULL,                          NULL,
217          0,              0 }
218 };
219 static int Xsublim_Sig_Last;
220
221
222 /* Functions *****************************************************************/
223
224 /* Defer signals to protect the server grab ================================ */
225 static void xsublim_Sig_Catch(int sig_Number)
226 {
227         /* BSD needs this reset each time, and it shouldn't hurt anything
228            else */
229         signal(sig_Number,xsublim_Sig_Catch);
230         Xsublim_Sig_Last = sig_Number;
231 }
232
233 /* This was all basically swiped from driver/remote.c and util/vroot.h */
234 static Window xsublim_Ss_GetWindow(Display* ss_Display)
235 {
236   Screen *s = DefaultScreenOfDisplay (ss_Display);
237   Window root = XRootWindowOfScreen (s);
238   Window vroot = VirtualRootWindowOfScreen (s);
239   if (root == vroot)
240     return 0;
241   else
242     return vroot;
243 }
244
245 /* Main ==================================================================== */
246 static XErrorHandler Xsublim_Sh_Handler = NULL;
247 static int           Xsublim_Sh_Status = 0;
248
249 static int xsublim_Sh_Handler(Display* handle_Display,
250             XErrorEvent* handle_Error)
251 {
252         if (handle_Error->error_code == BadMatch)
253         {
254                 Xsublim_Sh_Status = BadMatch;
255                 return 0;
256         }
257         if (Xsublim_Sh_Handler == NULL)
258         {
259                 fprintf(stderr,"%s: ",progname);
260                 abort();
261         }
262         return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
263 }
264
265
266 int main(int argc,char* argv[])
267 {
268         XtAppContext app;
269         int               sig_Number;
270         int               sig_Signal[] =
271         {
272                 SIGHUP,
273                 SIGINT,
274                 SIGQUIT,
275                 SIGILL,
276                 SIGTRAP,
277 #if defined(SIGIOT)
278                 SIGIOT,
279 #endif
280                 SIGABRT,
281 #if defined(SIGEMT)
282                 SIGEMT,
283 #endif
284                 SIGFPE,
285                 SIGBUS,
286                 SIGSEGV,
287 #if defined(SIGSYS)
288                 SIGSYS,
289 #endif
290                 SIGTERM,
291 #if defined(SIGXCPU)
292                 SIGXCPU,
293 #endif
294 #if defined(SIGXFSZ)
295                 SIGXFSZ,
296 #endif
297 #if defined(SIGDANGER)
298                 SIGDANGER,
299 #endif
300                 -1
301         };
302         Widget            app_App;
303         Display*          dpy;
304         Window            win_Root;
305         XWindowAttributes attr_Win;
306         XGCValues         gc_ValFore;
307         XGCValues         gc_ValBack;
308         GC                gc_GcFore;
309         GC                gc_GcBack;
310         XFontStruct*      font_Font;
311         char*             font_List[] =
312         {
313                 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
314                 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
315                 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
316                 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
317                 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
318                 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
319                 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
320                 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
321                 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
322                 "fixed",
323                 NULL
324         };
325         int               font_Index;
326         int               text_Length;
327         int               text_X;
328         int               text_Y;
329         int               text_Width;
330         int               text_Height;
331         char*             text_List[XSUBLIM_TEXT_COUNT];
332         int               text_Used[XSUBLIM_TEXT_COUNT];
333         char              text_Text[XSUBLIM_TEXT_LENGTH+1];
334         char*             text_Phrase;
335         char*             text_Word;
336         int               text_Index;
337         int               text_Item;
338         int               text_Count;
339         struct
340         {
341                 int outline_X;
342                 int outline_Y;
343         }                 text_Outline[] =
344         {
345                 { -1,-1 },
346                 {  1,-1 },
347                 { -1, 1 },
348                 {  1, 1 },
349                 {  0, 0 }
350         };
351         int               text_OutlineIndex;
352         XImage*           image_Image = NULL;
353         int               image_X = 0;
354         int               image_Y = 0;
355         int               image_Width = 0;
356         int               image_Height = 0;
357         int               arg_Count;
358         int               arg_FlagCenter;
359         int               arg_FlagOutline;
360         int               arg_FlagScreensaver;
361         int               arg_FlagRandom;
362         int               arg_DelayShow;
363         int               arg_DelayWord;
364         int               arg_DelayPhraseMin;
365         int               arg_DelayPhraseMax;
366         char*             arg_Text;
367         char*             arg_Source;
368
369         /* Set-up ---------------------------------------------------------- */
370
371         /* Catch signals */
372         Xsublim_Sig_Last = -1;
373         for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
374         {
375                 signal(sig_Number,xsublim_Sig_Catch);
376         }
377
378         /* Randomize -- only need to do this here because this program
379            doesn't use the `screenhack.h' or `lockmore.h' APIs. */
380 # undef ya_rand_init
381         ya_rand_init (0);
382
383         /* Handle all the X nonsense */
384 #if defined(__sgi)
385         SgiUseSchemes("none");
386 #endif
387         for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
388         {
389                 ;
390         }
391         app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
392          defaults,0,0);
393
394         /* jwz */
395         if (argc > 1)
396           {
397             int x = 18;
398             int end = 78;
399             int i;
400             int count = (sizeof(options)/sizeof(*options))-1;
401             fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
402             fprintf (stderr, "Options include: ");
403             for (i = 0; i < count; i++)
404               {
405                 char *sw = options [i].option;
406                 Bool argp = (options [i].argKind == XrmoptionSepArg);
407                 int size = strlen (sw) + (argp ? 6 : 0) + 2;
408                 if (x + size >= end)
409                   {
410                     fprintf (stderr, "\n\t\t ");
411                     x = 18;
412                   }
413                 x += size;
414                 fprintf (stderr, "%s", sw);
415                 if (argp) fprintf (stderr, " <arg>");
416                 if (i != count-1) fprintf (stderr, ", ");
417               }
418             fprintf (stderr, ".\n");
419             exit (-1);
420           }
421
422         dpy = XtDisplay(app_App);
423         XtGetApplicationNameAndClass(dpy,&progname,&progclass);
424         win_Root = RootWindowOfScreen(XtScreen(app_App));
425         XtDestroyWidget(app_App);
426
427         /* Get the arguments */
428         arg_FlagCenter = get_boolean_resource(dpy, XSUBLIM_ARG_CENTER,"Boolean");
429         arg_FlagOutline = get_boolean_resource(dpy, XSUBLIM_ARG_OUTLINE,"Boolean");
430         arg_FlagScreensaver = get_boolean_resource(dpy, XSUBLIM_ARG_SCREENSAVER,
431          "Boolean");
432         arg_FlagRandom = get_boolean_resource(dpy, XSUBLIM_ARG_RANDOM,"Boolean");
433         arg_DelayShow = get_integer_resource(dpy, XSUBLIM_ARG_DELAYSHOW,"Integer");
434         arg_DelayWord = get_integer_resource(dpy, XSUBLIM_ARG_DELAYWORD,"Integer");
435         arg_DelayPhraseMin = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMIN,
436          "Integer");
437         arg_DelayPhraseMax = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMAX,
438          "Integer");
439         if (arg_DelayPhraseMax < arg_DelayPhraseMin)
440         {
441                 arg_DelayPhraseMax = arg_DelayPhraseMin;
442         }
443
444         /* Get the phrases */
445         text_Index = 0;
446         text_Item = 0;
447         text_Count = 0;
448         memset(text_Used,0,sizeof(text_Used));
449         arg_Source = get_string_resource(dpy, XSUBLIM_ARG_FILE,"Filename");
450         if (arg_Source != NULL)
451         {
452                 FILE*       file_Fs;
453                 struct stat file_Stat;
454
455                 file_Fs = fopen(arg_Source,"rb");
456                 if (file_Fs == NULL)
457                 {
458                         fprintf(stderr,"%s: Could not open '%s'\n",progname,
459                          arg_Source);
460                         exit(-1);
461                 }
462                 if (fstat(fileno(file_Fs),&file_Stat) != 0)
463                 {
464                         fprintf(stderr,"%s: Could not stat '%s'\n",progname,
465                          arg_Source);
466                         exit(-1);
467                 }
468                 arg_Text = calloc(1,file_Stat.st_size+1);
469                 if (arg_Text != NULL)
470                 {
471                         if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
472                         {
473                                 fprintf(stderr,"%s: Could not read '%s'\n",
474                                  progname,arg_Source);
475                                 exit(-1);
476                         }
477                 }
478                 fclose(file_Fs);
479         }
480         else
481         {
482                 arg_Source = get_string_resource(dpy, XSUBLIM_ARG_PROGRAM,
483                  "Executable");
484                 if (arg_Source != NULL)
485                 {
486                         char* exe_Command = calloc(1,strlen(arg_Source)+10);
487                         FILE* exe_Fs;
488
489                         if (exe_Command == NULL)
490                         {
491                                 fprintf(stderr,
492                                  "%s: Could not allocate space for '%s'\n",
493                                  progname,arg_Source);
494                                 exit(-1);
495                         }
496                         sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
497
498                         exe_Fs = popen(exe_Command,"r");
499                         if (exe_Fs == NULL)
500                         {
501                                 fprintf(stderr,"%s: Could not run '%s'\n",
502                                  progname,arg_Source);
503                                 exit(-1);
504                         }
505                         arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
506                         if (arg_Text != NULL)
507                         {
508                                 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
509                                  exe_Fs) <= 0)
510                                 {
511                                         fprintf(stderr,
512                                          "%s: Could not read output of '%s'\n",
513                                          progname,arg_Source);
514                                         exit(-1);
515                                 }
516                                 if (
517                                  strstr(arg_Text,": not found") ||
518                                  strstr(arg_Text,": Not found") ||
519                                  strstr(arg_Text,": command not found") ||
520                                  strstr(arg_Text,": Command not found"))
521                                 {
522                                         fprintf(stderr,
523                                          "%s: Could not find '%s'\n",
524                                          progname,arg_Source);
525                                         exit(-1);
526                                 }
527                         }
528                         fclose(exe_Fs);
529                 }
530                 else
531                 {
532                         arg_Text =
533                          get_string_resource(dpy, XSUBLIM_ARG_PHRASES,"Phrases");
534                         if (arg_Text != NULL)
535                         {
536                                 arg_Text = strdup(arg_Text);
537                         }
538                 }
539         }
540         if (arg_Text != NULL)
541         {
542                 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
543                  (text_Count < XSUBLIM_TEXT_COUNT))
544                 {
545                         arg_Text = NULL;
546                         text_List[text_Count] = text_Phrase;
547                         text_Count++;
548                 }
549                 text_List[text_Count] = NULL;
550         }
551         if (text_Count == 0)
552         {
553                 fprintf(stderr,"%s: No text to display\n",progname);
554                 exit(-1);
555         }
556
557         /* Load the font */
558         font_Font = XLoadQueryFont(dpy,
559          get_string_resource(dpy, XSUBLIM_ARG_FONT,"Font"));
560         font_Index = 0;
561         while ((font_Font == NULL) && (font_List[font_Index] != NULL))
562         {
563                 font_Font = XLoadQueryFont(dpy,font_List[font_Index]);
564                 font_Index++;
565         }
566         if (font_Font == NULL)
567         {
568                 fprintf(stderr,"%s: Couldn't load a font\n",progname);
569                 exit(-1);
570         }
571
572         /* Create the GCs */
573         XGetWindowAttributes(dpy,win_Root,&attr_Win);
574         gc_ValFore.font = font_Font->fid;
575         gc_ValFore.foreground = get_pixel_resource(dpy,
576                                                    attr_Win.colormap,
577                                                    "foreground","Foreground");
578         gc_ValFore.background = get_pixel_resource(dpy,
579                                                    attr_Win.colormap,
580                                                    "background","Background");
581         gc_ValFore.subwindow_mode = IncludeInferiors;
582         gc_GcFore = XCreateGC(dpy,win_Root,
583          (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
584         gc_ValBack.font = font_Font->fid;
585         gc_ValBack.foreground = get_pixel_resource(dpy,
586                                                    attr_Win.colormap,
587                                                    "background","Background");
588         gc_ValBack.background = get_pixel_resource(dpy,
589                                                    attr_Win.colormap,
590                                                    "foreground","Foreground");
591         gc_ValBack.subwindow_mode = IncludeInferiors;
592         gc_GcBack = XCreateGC(dpy,win_Root,
593          (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
594
595         /* Loop ------------------------------------------------------------ */
596         while (Xsublim_Sig_Last == -1)
597         {
598                 /* Once-per-phrase stuff ----------------------------------- */
599
600                 /* If we're waiting for a screensaver... */
601                 if (arg_FlagScreensaver != FALSE)
602                 {
603                         /* Find the screensaver's window */
604                         win_Root = xsublim_Ss_GetWindow(dpy);
605                         if (win_Root == 0)
606                         {
607                                 usleep(30000000);
608                                 continue;
609                         }
610                 }
611
612                 /* Pick the next phrase */
613                 if (arg_FlagRandom != FALSE)
614                 {
615                         text_Item = random()%text_Count;
616                         text_Index = 0;
617                 }
618                 while (text_Used[text_Item] != FALSE)
619                 {
620                         text_Index++;
621                         text_Item++;
622                         if (text_Index == text_Count)
623                         {
624                                 text_Index = 0;
625                                 memset(text_Used,0,sizeof(text_Used));
626                         }
627                         if (text_List[text_Item] == NULL)
628                         {
629                                 text_Item = 0;
630                         }
631                 }
632                 text_Used[text_Item] = TRUE;
633                 strncpy(text_Text,text_List[text_Item],
634                  XSUBLIM_TEXT_LENGTH);
635                 text_Phrase = text_Text;
636
637                 /* Run through the phrase */
638                 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
639                  (Xsublim_Sig_Last == -1))
640                 {
641                         text_Phrase = NULL;
642
643                         /* Once-per-word stuff ----------------------------- */
644
645                         /* Find the text's position */
646                         XGetWindowAttributes(dpy,win_Root,&attr_Win);
647                         text_Length = strlen(text_Word);
648                         text_Width = XTextWidth(font_Font,text_Word,
649                          text_Length)+XSUBLIM_TEXT_OUTLINE*2;
650                         text_Height = font_Font->ascent+font_Font->descent+1+
651                          XSUBLIM_TEXT_OUTLINE*2;
652                         if (arg_FlagCenter == FALSE)
653                         {
654                                 text_X = random()%(attr_Win.width-text_Width);
655                                 text_Y = random()%(attr_Win.height-
656                                  text_Height);
657                         }
658                         else
659                         {
660                                 text_X = (attr_Win.width/2)-(text_Width/2);
661                                 text_Y = (attr_Win.height/2)-(text_Height/2);
662                         }
663
664                         /* Find the image's position (and pad it out slightly,
665                            otherwise bits of letter get left behind -- are
666                            there boundry issues I don't know about?) */
667                         image_X = text_X-16;
668                         image_Y = text_Y;
669                         image_Width = text_Width+32;
670                         image_Height = text_Height;
671                         if (image_X < 0)
672                         {
673                                 image_X = 0;
674                         }
675                         if (image_Y < 0)
676                         {
677                                 image_Y = 0;
678                         }
679                         if (image_X+image_Width > attr_Win.width)
680                         {
681                                 image_Width = attr_Win.width-image_X;
682                         }
683                         if (image_Y+image_Height > attr_Win.height)
684                         {
685                                 image_Height = attr_Win.height-image_Y;
686                         }
687
688                         /* Influence people for our own ends --------------- */
689
690                         /* Grab the server -- we can't let anybody draw over
691                            us */
692                         XSync(dpy,FALSE);
693                         XGrabServer(dpy);
694                         XSync(dpy,FALSE);
695
696                         /* Set up an error handler that ignores BadMatches --
697                            since the screensaver can take its window away at
698                            any time, any call that uses it might choke */
699                         Xsublim_Sh_Status = 0;
700                         Xsublim_Sh_Handler =
701                          XSetErrorHandler(xsublim_Sh_Handler);
702
703                         /* Save the current background */
704                         image_Image = XGetImage(dpy,win_Root,image_X,
705                          image_Y,image_Width,image_Height,~0L,ZPixmap);
706
707                         /* If we've successfully saved the background... */
708                         if (image_Image != NULL)
709                         {
710                                 if (Xsublim_Sh_Status == 0)
711                                 {
712                                         /* Draw the outline */
713                                         if (arg_FlagOutline != FALSE)
714                                         {
715                                                 for (text_OutlineIndex = 0;
716                                                  text_Outline[
717                                                  text_OutlineIndex].outline_X
718                                                  != 0;text_OutlineIndex++)
719                                                 {
720                                                         /* Y'know, eight
721                                                            character tabs and
722                                                            descriptive variable
723                                                            names become
724                                                            annoying at some
725                                                            point... */
726                                                         XDrawString(
727                                                          dpy,
728                                                          win_Root,gc_GcBack,
729                                                          text_X+text_Outline[
730                                                          text_OutlineIndex].
731                                                          outline_X*
732                                                          XSUBLIM_TEXT_OUTLINE,
733                                                          text_Y+
734                                                          (font_Font->ascent)+
735                                                          text_Outline[
736                                                          text_OutlineIndex].
737                                                          outline_Y*
738                                                          XSUBLIM_TEXT_OUTLINE,
739                                                          text_Word,
740                                                          text_Length);
741                                                 }
742                                         }
743
744                                         /* Draw the word */
745                                         XDrawString(dpy,win_Root,
746                                          gc_GcFore,text_X,
747                                          text_Y+(font_Font->ascent),text_Word,
748                                          text_Length);
749                                 }
750                                 if (Xsublim_Sh_Status == 0)
751                                 {
752                                         /* Wait a bit */
753                                         XSync(dpy,FALSE);
754                                         if (Xsublim_Sig_Last == -1)
755                                         {
756                                                 usleep(arg_DelayShow);
757                                         }
758         
759                                         /* Restore the background */
760                                         XPutImage(dpy,win_Root,
761                                          gc_GcFore,image_Image,0,0,image_X,
762                                          image_Y,image_Width,image_Height);
763                                 }
764
765                                 /* Free the image */
766                                 XDestroyImage(image_Image);
767                         }
768
769                         /* Restore the error handler, ungrab the server */
770                         XSync(dpy,FALSE);
771                         XSetErrorHandler(Xsublim_Sh_Handler);
772                         XUngrabServer(dpy);
773                         XSync(dpy,FALSE);
774
775                         /* Pause between words */
776                         if (Xsublim_Sig_Last == -1)
777                         {
778                                 usleep(arg_DelayWord);
779                         }
780                 }
781
782                 /* Pause between phrases */
783                 if (Xsublim_Sig_Last == -1)
784                 {
785                         usleep(random()%(arg_DelayPhraseMax-
786                          arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
787                 }
788         }
789
790         /* Exit ------------------------------------------------------------ */
791         for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
792         {
793                 signal(sig_Number,SIG_DFL);
794         }
795         kill(getpid(),Xsublim_Sig_Last);
796
797         return 0;
798 }