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