1 /****************************************************************************
3 * xsublim -- Submit. Conform. Obey. *
5 * Copyright (c) 1999 - 2000 Greg Knauss (greg@eod.com) *
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) *
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 *
19 * Stare into the subliminal for as long as you can... *
21 *****************************************************************************/
24 /* Warnings *******************************************************************
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.
31 /* Arguments ******************************************************************
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
51 /* Changelog ******************************************************************
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
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"
86 /* Includes ******************************************************************/
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>
102 #include <X11/Xproto.h>
104 #include <X11/SGIScheme.h>
108 #include "yarandom.h"
109 #include "resources.h"
113 /* Globals *******************************************************************/
117 char* progclass = XSUBLIM_NAME;
120 __extension__ /* don't warn about "string length is greater than the
121 length ISO C89 compilers are required to support"
122 in the following string constant... */
127 ".background: #000000",
128 ".foreground: #FFFFFF",
129 "*" XSUBLIM_ARG_PHRASES ":"
133 "OBEY. OBEY. OBEY.\\n"
142 "Despair quietly.\\n"
144 "You are being watched.\\n"
145 "You will be punished.\\n"
146 "You serve no purpose.\\n"
147 "Your contributions are ignored.\\n"
148 "They are laughing at you.\\n"
149 "They lied to you.\\n"
150 "They read your mail.\\n"
155 "You are a prisoner.\\n"
156 "You are helpless.\\n"
157 "You are diseased.\\n"
158 "Fear the unknown.\\n"
159 "Happiness follows obedience.\\n"
160 "Ignorance is strength.\\n"
162 "Freedom is slavery.\\n"
163 "Abandon all hope.\\n"
164 "You will be assimilated.\\n"
165 "Resistance is futile.\\n"
166 "Resistance is useless.\\n"
169 "What's that smell?\\n"
170 "All praise the company.\\n"
172 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
173 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
174 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
175 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
176 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
177 "*" XSUBLIM_ARG_RANDOM ": true",
178 "*" XSUBLIM_ARG_SCREENSAVER ": true",
179 "*" XSUBLIM_ARG_OUTLINE": true",
180 "*" XSUBLIM_ARG_CENTER": true",
183 XrmOptionDescRec options[] =
185 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
187 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
189 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
191 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
193 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
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,
202 {"-" XSUBLIM_ARG_PROGRAM, "." XSUBLIM_ARG_PROGRAM,
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"},
220 static int Xsublim_Sig_Last;
223 /* Functions *****************************************************************/
225 /* Defer signals to protect the server grab ================================ */
226 void xsublim_Sig_Catch(int sig_Number)
228 /* BSD needs this reset each time, and it shouldn't hurt anything
230 signal(sig_Number,xsublim_Sig_Catch);
231 Xsublim_Sig_Last = sig_Number;
234 /* This was all basically swiped from driver/remote.c and util/vroot.h */
235 static Window xsublim_Ss_GetWindow(Display* ss_Display)
237 Screen *s = DefaultScreenOfDisplay (ss_Display);
238 Window root = XRootWindowOfScreen (s);
239 Window vroot = VirtualRootWindowOfScreen (s);
246 /* Main ==================================================================== */
247 static XErrorHandler Xsublim_Sh_Handler = NULL;
248 static int Xsublim_Sh_Status = 0;
250 static int xsublim_Sh_Handler(Display* handle_Display,
251 XErrorEvent* handle_Error)
253 if (handle_Error->error_code == BadMatch)
255 Xsublim_Sh_Status = BadMatch;
258 if (Xsublim_Sh_Handler == NULL)
260 fprintf(stderr,"%s: ",progname);
263 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
265 int main(int argc,char* argv[])
295 #if defined(SIGDANGER)
301 Display* disp_Display;
303 XWindowAttributes attr_Win;
304 XGCValues gc_ValFore;
305 XGCValues gc_ValBack;
308 XFontStruct* font_Font;
311 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
312 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
313 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
314 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
315 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
316 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
317 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
318 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
319 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
320 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
321 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
331 char* text_List[XSUBLIM_TEXT_COUNT];
332 int text_Used[XSUBLIM_TEXT_COUNT];
333 char text_Text[XSUBLIM_TEXT_LENGTH+1];
351 int text_OutlineIndex;
352 XImage* image_Image = NULL;
356 int image_Height = 0;
360 int arg_FlagScreensaver;
364 int arg_DelayPhraseMin;
365 int arg_DelayPhraseMax;
369 /* Set-up ---------------------------------------------------------- */
372 Xsublim_Sig_Last = -1;
373 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
375 signal(sig_Number,xsublim_Sig_Catch);
378 /* Randomize -- only need to do this here because this program
379 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
383 /* Handle all the X nonsense */
385 SgiUseSchemes("none");
387 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
391 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
400 int count = (sizeof(options)/sizeof(*options))-1;
401 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
402 fprintf (stderr, "Options include: ");
403 for (i = 0; i < count; i++)
405 char *sw = options [i].option;
406 Bool argp = (options [i].argKind == XrmoptionSepArg);
407 int size = strlen (sw) + (argp ? 6 : 0) + 2;
410 fprintf (stderr, "\n\t\t ");
414 fprintf (stderr, "%s", sw);
415 if (argp) fprintf (stderr, " <arg>");
416 if (i != count-1) fprintf (stderr, ", ");
418 fprintf (stderr, ".\n");
422 disp_Display = XtDisplay(app_App);
423 db = XtDatabase(disp_Display);
424 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
425 win_Root = RootWindowOfScreen(XtScreen(app_App));
426 XtDestroyWidget(app_App);
428 /* Get the arguments */
429 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
430 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
431 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
433 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
434 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
435 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
436 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
438 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
440 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
442 arg_DelayPhraseMax = arg_DelayPhraseMin;
445 /* Get the phrases */
449 memset(text_Used,0,sizeof(text_Used));
450 arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
451 if (arg_Source != NULL)
454 struct stat file_Stat;
456 file_Fs = fopen(arg_Source,"rb");
459 fprintf(stderr,"%s: Could not open '%s'\n",progname,
463 if (fstat(fileno(file_Fs),&file_Stat) != 0)
465 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
469 arg_Text = calloc(1,file_Stat.st_size+1);
470 if (arg_Text != NULL)
472 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
474 fprintf(stderr,"%s: Could not read '%s'\n",
475 progname,arg_Source);
483 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
485 if (arg_Source != NULL)
487 char* exe_Command = calloc(1,strlen(arg_Source)+10);
490 if (exe_Command == NULL)
493 "%s: Could not allocate space for '%s'\n",
494 progname,arg_Source);
497 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
499 exe_Fs = popen(exe_Command,"r");
502 fprintf(stderr,"%s: Could not run '%s'\n",
503 progname,arg_Source);
506 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
507 if (arg_Text != NULL)
509 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
513 "%s: Could not read output of '%s'\n",
514 progname,arg_Source);
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"))
524 "%s: Could not find '%s'\n",
525 progname,arg_Source);
534 get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
535 if (arg_Text != NULL)
537 arg_Text = strdup(arg_Text);
541 if (arg_Text != NULL)
543 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
544 (text_Count < XSUBLIM_TEXT_COUNT))
547 text_List[text_Count] = text_Phrase;
550 text_List[text_Count] = NULL;
554 fprintf(stderr,"%s: No text to display\n",progname);
559 font_Font = XLoadQueryFont(disp_Display,
560 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
562 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
564 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
567 if (font_Font == NULL)
569 fprintf(stderr,"%s: Couldn't load a font\n",progname);
574 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
575 gc_ValFore.font = font_Font->fid;
576 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
577 disp_Display,attr_Win.colormap);
578 gc_ValFore.background = get_pixel_resource("background","Background",
579 disp_Display,attr_Win.colormap);
580 gc_ValFore.subwindow_mode = IncludeInferiors;
581 gc_GcFore = XCreateGC(disp_Display,win_Root,
582 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
583 gc_ValBack.font = font_Font->fid;
584 gc_ValBack.foreground = get_pixel_resource("background","Background",
585 disp_Display,attr_Win.colormap);
586 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
587 disp_Display,attr_Win.colormap);
588 gc_ValBack.subwindow_mode = IncludeInferiors;
589 gc_GcBack = XCreateGC(disp_Display,win_Root,
590 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
592 /* Loop ------------------------------------------------------------ */
593 while (Xsublim_Sig_Last == -1)
595 /* Once-per-phrase stuff ----------------------------------- */
597 /* If we're waiting for a screensaver... */
598 if (arg_FlagScreensaver != FALSE)
600 /* Find the screensaver's window */
601 win_Root = xsublim_Ss_GetWindow(disp_Display);
609 /* Pick the next phrase */
610 if (arg_FlagRandom != FALSE)
612 text_Item = random()%text_Count;
615 while (text_Used[text_Item] != FALSE)
619 if (text_Index == text_Count)
622 memset(text_Used,0,sizeof(text_Used));
624 if (text_List[text_Item] == NULL)
629 text_Used[text_Item] = TRUE;
630 strncpy(text_Text,text_List[text_Item],
631 XSUBLIM_TEXT_LENGTH);
632 text_Phrase = text_Text;
634 /* Run through the phrase */
635 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
636 (Xsublim_Sig_Last == -1))
640 /* Once-per-word stuff ----------------------------- */
642 /* Find the text's position */
643 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
644 text_Length = strlen(text_Word);
645 text_Width = XTextWidth(font_Font,text_Word,
646 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
647 text_Height = font_Font->ascent+font_Font->descent+1+
648 XSUBLIM_TEXT_OUTLINE*2;
649 if (arg_FlagCenter == FALSE)
651 text_X = random()%(attr_Win.width-text_Width);
652 text_Y = random()%(attr_Win.height-
657 text_X = (attr_Win.width/2)-(text_Width/2);
658 text_Y = (attr_Win.height/2)-(text_Height/2);
661 /* Find the image's position (and pad it out slightly,
662 otherwise bits of letter get left behind -- are
663 there boundry issues I don't know about?) */
666 image_Width = text_Width+32;
667 image_Height = text_Height;
676 if (image_X+image_Width > attr_Win.width)
678 image_Width = attr_Win.width-image_X;
680 if (image_Y+image_Height > attr_Win.height)
682 image_Height = attr_Win.height-image_Y;
685 /* Influence people for our own ends --------------- */
687 /* Grab the server -- we can't let anybody draw over
689 XSync(disp_Display,FALSE);
690 XGrabServer(disp_Display);
691 XSync(disp_Display,FALSE);
693 /* Set up an error handler that ignores BadMatches --
694 since the screensaver can take its window away at
695 any time, any call that uses it might choke */
696 Xsublim_Sh_Status = 0;
698 XSetErrorHandler(xsublim_Sh_Handler);
700 /* Save the current background */
701 image_Image = XGetImage(disp_Display,win_Root,image_X,
702 image_Y,image_Width,image_Height,~0L,ZPixmap);
704 /* If we've successfully saved the background... */
705 if (image_Image != NULL)
707 if (Xsublim_Sh_Status == 0)
709 /* Draw the outline */
710 if (arg_FlagOutline != FALSE)
712 for (text_OutlineIndex = 0;
714 text_OutlineIndex].outline_X
715 != 0;text_OutlineIndex++)
729 XSUBLIM_TEXT_OUTLINE,
735 XSUBLIM_TEXT_OUTLINE,
742 XDrawString(disp_Display,win_Root,
744 text_Y+(font_Font->ascent),text_Word,
747 if (Xsublim_Sh_Status == 0)
750 XSync(disp_Display,FALSE);
751 if (Xsublim_Sig_Last == -1)
753 usleep(arg_DelayShow);
756 /* Restore the background */
757 XPutImage(disp_Display,win_Root,
758 gc_GcFore,image_Image,0,0,image_X,
759 image_Y,image_Width,image_Height);
763 XDestroyImage(image_Image);
766 /* Restore the error handler, ungrab the server */
767 XSync(disp_Display,FALSE);
768 XSetErrorHandler(Xsublim_Sh_Handler);
769 XUngrabServer(disp_Display);
770 XSync(disp_Display,FALSE);
772 /* Pause between words */
773 if (Xsublim_Sig_Last == -1)
775 usleep(arg_DelayWord);
779 /* Pause between phrases */
780 if (Xsublim_Sig_Last == -1)
782 usleep(random()%(arg_DelayPhraseMax-
783 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
787 /* Exit ------------------------------------------------------------ */
788 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
790 signal(sig_Number,SIG_DFL);
792 kill(getpid(),Xsublim_Sig_Last);