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