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"
112 /* Globals *******************************************************************/
116 char* progclass = XSUBLIM_NAME;
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... */
126 ".background: #000000",
127 ".foreground: #FFFFFF",
128 "*" XSUBLIM_ARG_PHRASES ":"
132 "OBEY. OBEY. OBEY.\\n"
141 "Despair quietly.\\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"
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"
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"
168 "What's that smell?\\n"
169 "All praise the company.\\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",
182 XrmOptionDescRec options[] =
184 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
186 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
188 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
190 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
192 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
194 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
195 XrmoptionNoArg,"true"},
196 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
197 XrmoptionNoArg,"false"},
198 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
201 {"-" XSUBLIM_ARG_PROGRAM, "." XSUBLIM_ARG_PROGRAM,
204 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
205 XrmoptionNoArg,"true"},
206 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
207 XrmoptionNoArg,"false"},
208 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
209 XrmoptionNoArg,"true"},
210 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
211 XrmoptionNoArg,"false"},
212 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
213 XrmoptionNoArg,"true"},
214 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
215 XrmoptionNoArg,"false"},
219 static int Xsublim_Sig_Last;
222 /* Functions *****************************************************************/
224 /* Defer signals to protect the server grab ================================ */
225 void xsublim_Sig_Catch(int sig_Number)
227 /* BSD needs this reset each time, and it shouldn't hurt anything
229 signal(sig_Number,xsublim_Sig_Catch);
230 Xsublim_Sig_Last = sig_Number;
233 /* Get the screensaver's window ============================================ */
234 static XErrorHandler Xsublim_Ss_Handler = NULL;
235 static int Xsublim_Ss_Status;
237 /* This was all basically swiped from driver/remote.c and util/vroot.h */
238 static int xsublim_Ss_Handler(Display* handle_Display,
239 XErrorEvent* handle_Error)
241 if (handle_Error->error_code == BadWindow)
243 Xsublim_Ss_Status = BadWindow;
246 if (Xsublim_Ss_Handler == NULL)
248 fprintf(stderr,"%s: ",progname);
251 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
253 static Window xsublim_Ss_GetWindow(Display* ss_Display)
256 Window win_RootReturn;
260 unsigned int child_Count;
264 unsigned long prop_Count;
265 unsigned long prop_Bytes;
268 static Atom XA_SCREENSAVER_VERSION = -1;
269 static Atom __SWM_VROOT;
271 /* Assume bad things */
276 if (XA_SCREENSAVER_VERSION == -1)
278 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
279 "_SCREENSAVER_VERSION",FALSE);
280 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
283 /* Find a screensaver window */
284 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
285 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
286 &win_Child,&child_Count) != FALSE)
289 (win_Root == win_RootReturn) &&
291 (win_Child != NULL) &&
294 for (child_Index = 0;child_Index < child_Count;
297 XSync(ss_Display,FALSE);
298 Xsublim_Ss_Status = 0;
300 XSetErrorHandler(xsublim_Ss_Handler);
302 prop_Status = XGetWindowProperty(ss_Display,
303 win_Child[child_Index],XA_SCREENSAVER_VERSION,
304 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
305 &prop_Count,&prop_Bytes,
306 (unsigned char**)&prop_Value);
307 XSync(ss_Display,FALSE);
308 XSetErrorHandler(Xsublim_Ss_Handler);
309 if (prop_Value != NULL)
313 if (Xsublim_Ss_Status == BadWindow)
315 prop_Status = BadWindow;
317 if ((prop_Status == Success) &&
320 /* See if it's a virtual root */
323 XGetWindowProperty(ss_Display,
324 win_Child[child_Index],__SWM_VROOT,0,
325 1,FALSE,XA_WINDOW,&prop_Type,
326 &prop_Format,&prop_Count,&prop_Bytes,
327 (unsigned char**)&prop_Value);
328 if (prop_Value != NULL)
332 if ((prop_Status == Success) &&
336 win_Child[child_Index];
342 if (win_Child != NULL)
349 /* Main ==================================================================== */
350 static XErrorHandler Xsublim_Sh_Handler = NULL;
351 static int Xsublim_Sh_Status = 0;
353 static int xsublim_Sh_Handler(Display* handle_Display,
354 XErrorEvent* handle_Error)
356 if (handle_Error->error_code == BadMatch)
358 Xsublim_Sh_Status = BadMatch;
361 if (Xsublim_Sh_Handler == NULL)
363 fprintf(stderr,"%s: ",progname);
366 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
368 int main(int argc,char* argv[])
398 #if defined(SIGDANGER)
404 Display* disp_Display;
406 XWindowAttributes attr_Win;
407 XGCValues gc_ValFore;
408 XGCValues gc_ValBack;
411 XFontStruct* font_Font;
414 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
415 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
416 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
417 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
418 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
419 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
420 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
421 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
422 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
423 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
424 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
434 char* text_List[XSUBLIM_TEXT_COUNT];
435 int text_Used[XSUBLIM_TEXT_COUNT];
436 char text_Text[XSUBLIM_TEXT_LENGTH+1];
454 int text_OutlineIndex;
455 XImage* image_Image = NULL;
459 int image_Height = 0;
463 int arg_FlagScreensaver;
467 int arg_DelayPhraseMin;
468 int arg_DelayPhraseMax;
472 /* Set-up ---------------------------------------------------------- */
475 Xsublim_Sig_Last = -1;
476 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
478 signal(sig_Number,xsublim_Sig_Catch);
481 /* Randomize -- only need to do this here because this program
482 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
486 /* Handle all the X nonsense */
488 SgiUseSchemes("none");
490 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
494 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
503 int count = (sizeof(options)/sizeof(*options))-1;
504 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
505 fprintf (stderr, "Options include: ");
506 for (i = 0; i < count; i++)
508 char *sw = options [i].option;
509 Bool argp = (options [i].argKind == XrmoptionSepArg);
510 int size = strlen (sw) + (argp ? 6 : 0) + 2;
513 fprintf (stderr, "\n\t\t ");
517 fprintf (stderr, "%s", sw);
518 if (argp) fprintf (stderr, " <arg>");
519 if (i != count-1) fprintf (stderr, ", ");
521 fprintf (stderr, ".\n");
525 disp_Display = XtDisplay(app_App);
526 db = XtDatabase(disp_Display);
527 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
528 win_Root = RootWindowOfScreen(XtScreen(app_App));
529 XtDestroyWidget(app_App);
531 /* Get the arguments */
532 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
533 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
534 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
536 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
537 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
538 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
539 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
541 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
543 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
545 arg_DelayPhraseMax = arg_DelayPhraseMin;
548 /* Get the phrases */
552 memset(text_Used,0,sizeof(text_Used));
553 arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
554 if (arg_Source != NULL)
557 struct stat file_Stat;
559 file_Fs = fopen(arg_Source,"rb");
562 fprintf(stderr,"%s: Could not open '%s'\n",progname,
566 if (fstat(fileno(file_Fs),&file_Stat) != 0)
568 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
572 arg_Text = calloc(1,file_Stat.st_size+1);
573 if (arg_Text != NULL)
575 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
577 fprintf(stderr,"%s: Could not read '%s'\n",
578 progname,arg_Source);
586 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
588 if (arg_Source != NULL)
590 char* exe_Command = calloc(1,strlen(arg_Source)+10);
593 if (exe_Command == NULL)
596 "%s: Could not allocate space for '%s'\n",
597 progname,arg_Source);
600 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
602 exe_Fs = popen(exe_Command,"r");
605 fprintf(stderr,"%s: Could not run '%s'\n",
606 progname,arg_Source);
609 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
610 if (arg_Text != NULL)
612 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
616 "%s: Could not read output of '%s'\n",
617 progname,arg_Source);
621 strstr(arg_Text,": not found") ||
622 strstr(arg_Text,": Not found") ||
623 strstr(arg_Text,": command not found") ||
624 strstr(arg_Text,": Command not found"))
627 "%s: Could not find '%s'\n",
628 progname,arg_Source);
637 get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
638 if (arg_Text != NULL)
640 arg_Text = strdup(arg_Text);
644 if (arg_Text != NULL)
646 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
647 (text_Count < XSUBLIM_TEXT_COUNT))
650 text_List[text_Count] = text_Phrase;
653 text_List[text_Count] = NULL;
657 fprintf(stderr,"%s: No text to display\n",progname);
662 font_Font = XLoadQueryFont(disp_Display,
663 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
665 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
667 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
670 if (font_Font == NULL)
672 fprintf(stderr,"%s: Couldn't load a font\n",progname);
677 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
678 gc_ValFore.font = font_Font->fid;
679 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
680 disp_Display,attr_Win.colormap);
681 gc_ValFore.background = get_pixel_resource("background","Background",
682 disp_Display,attr_Win.colormap);
683 gc_ValFore.subwindow_mode = IncludeInferiors;
684 gc_GcFore = XCreateGC(disp_Display,win_Root,
685 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
686 gc_ValBack.font = font_Font->fid;
687 gc_ValBack.foreground = get_pixel_resource("background","Background",
688 disp_Display,attr_Win.colormap);
689 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
690 disp_Display,attr_Win.colormap);
691 gc_ValBack.subwindow_mode = IncludeInferiors;
692 gc_GcBack = XCreateGC(disp_Display,win_Root,
693 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
695 /* Loop ------------------------------------------------------------ */
696 while (Xsublim_Sig_Last == -1)
698 /* Once-per-phrase stuff ----------------------------------- */
700 /* If we're waiting for a screensaver... */
701 if (arg_FlagScreensaver != FALSE)
703 /* Find the screensaver's window */
704 win_Root = xsublim_Ss_GetWindow(disp_Display);
712 /* Pick the next phrase */
713 if (arg_FlagRandom != FALSE)
715 text_Item = random()%text_Count;
718 while (text_Used[text_Item] != FALSE)
722 if (text_Index == text_Count)
725 memset(text_Used,0,sizeof(text_Used));
727 if (text_List[text_Item] == NULL)
732 text_Used[text_Item] = TRUE;
733 strncpy(text_Text,text_List[text_Item],
734 XSUBLIM_TEXT_LENGTH);
735 text_Phrase = text_Text;
737 /* Run through the phrase */
738 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
739 (Xsublim_Sig_Last == -1))
743 /* Once-per-word stuff ----------------------------- */
745 /* Find the text's position */
746 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
747 text_Length = strlen(text_Word);
748 text_Width = XTextWidth(font_Font,text_Word,
749 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
750 text_Height = font_Font->ascent+font_Font->descent+1+
751 XSUBLIM_TEXT_OUTLINE*2;
752 if (arg_FlagCenter == FALSE)
754 text_X = random()%(attr_Win.width-text_Width);
755 text_Y = random()%(attr_Win.height-
760 text_X = (attr_Win.width/2)-(text_Width/2);
761 text_Y = (attr_Win.height/2)-(text_Height/2);
764 /* Find the image's position (and pad it out slightly,
765 otherwise bits of letter get left behind -- are
766 there boundry issues I don't know about?) */
769 image_Width = text_Width+32;
770 image_Height = text_Height;
779 if (image_X+image_Width > attr_Win.width)
781 image_Width = attr_Win.width-image_X;
783 if (image_Y+image_Height > attr_Win.height)
785 image_Height = attr_Win.height-image_Y;
788 /* Influence people for our own ends --------------- */
790 /* Grab the server -- we can't let anybody draw over
792 XSync(disp_Display,FALSE);
793 XGrabServer(disp_Display);
794 XSync(disp_Display,FALSE);
796 /* Set up an error handler that ignores BadMatches --
797 since the screensaver can take its window away at
798 any time, any call that uses it might choke */
799 Xsublim_Sh_Status = 0;
801 XSetErrorHandler(xsublim_Sh_Handler);
803 /* Save the current background */
804 image_Image = XGetImage(disp_Display,win_Root,image_X,
805 image_Y,image_Width,image_Height,~0L,ZPixmap);
807 /* If we've successfully saved the background... */
808 if (image_Image != NULL)
810 if (Xsublim_Sh_Status == 0)
812 /* Draw the outline */
813 if (arg_FlagOutline != FALSE)
815 for (text_OutlineIndex = 0;
817 text_OutlineIndex].outline_X
818 != 0;text_OutlineIndex++)
832 XSUBLIM_TEXT_OUTLINE,
838 XSUBLIM_TEXT_OUTLINE,
845 XDrawString(disp_Display,win_Root,
847 text_Y+(font_Font->ascent),text_Word,
850 if (Xsublim_Sh_Status == 0)
853 XSync(disp_Display,FALSE);
854 if (Xsublim_Sig_Last == -1)
856 usleep(arg_DelayShow);
859 /* Restore the background */
860 XPutImage(disp_Display,win_Root,
861 gc_GcFore,image_Image,0,0,image_X,
862 image_Y,image_Width,image_Height);
866 XDestroyImage(image_Image);
869 /* Restore the error handler, ungrab the server */
870 XSync(disp_Display,FALSE);
871 XSetErrorHandler(Xsublim_Sh_Handler);
872 XUngrabServer(disp_Display);
873 XSync(disp_Display,FALSE);
875 /* Pause between words */
876 if (Xsublim_Sig_Last == -1)
878 usleep(arg_DelayWord);
882 /* Pause between phrases */
883 if (Xsublim_Sig_Last == -1)
885 usleep(random()%(arg_DelayPhraseMax-
886 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
890 /* Exit ------------------------------------------------------------ */
891 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
893 signal(sig_Number,SIG_DFL);
895 kill(getpid(),Xsublim_Sig_Last);