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