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 ".background: #000000",
120 ".foreground: #FFFFFF",
121 "*" XSUBLIM_ARG_PHRASES ":"
125 "OBEY. OBEY. OBEY.\\n"
134 "Despair quietly.\\n"
136 "You are being watched.\\n"
137 "You will be punished.\\n"
138 "You serve no purpose.\\n"
139 "Your contributions are ignored.\\n"
140 "They are laughing at you.\\n"
141 "They lied to you.\\n"
142 "They read your mail.\\n"
147 "You are a prisoner.\\n"
148 "You are helpless.\\n"
149 "You are diseased.\\n"
150 "Fear the unknown.\\n"
151 "Happiness follows obedience.\\n"
152 "Ignorance is strength.\\n"
154 "Freedom is slavery.\\n"
155 "Abandon all hope.\\n"
156 "You will be assimilated.\\n"
157 "Resistance is futile.\\n"
158 "Resistance is useless.\\n"
161 "What's that smell?\\n"
162 "All praise the company.\\n"
164 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
165 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
166 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
167 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
168 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
169 "*" XSUBLIM_ARG_RANDOM ": true",
170 "*" XSUBLIM_ARG_SCREENSAVER ": true",
171 "*" XSUBLIM_ARG_OUTLINE": true",
172 "*" XSUBLIM_ARG_CENTER": true",
175 XrmOptionDescRec options[] =
177 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
179 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
181 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
183 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
185 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
187 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
188 XrmoptionNoArg,"true"},
189 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
190 XrmoptionNoArg,"false"},
191 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
194 {"-" XSUBLIM_ARG_PROGRAM, "." XSUBLIM_ARG_PROGRAM,
197 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
198 XrmoptionNoArg,"true"},
199 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
200 XrmoptionNoArg,"false"},
201 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
202 XrmoptionNoArg,"true"},
203 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
204 XrmoptionNoArg,"false"},
205 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
206 XrmoptionNoArg,"true"},
207 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
208 XrmoptionNoArg,"false"},
212 static int Xsublim_Sig_Last;
215 /* Functions *****************************************************************/
217 /* Defer signals to protect the server grab ================================ */
218 void xsublim_Sig_Catch(int sig_Number)
220 /* BSD needs this reset each time, and it shouldn't hurt anything
222 signal(sig_Number,xsublim_Sig_Catch);
223 Xsublim_Sig_Last = sig_Number;
226 /* Get the screensaver's window ============================================ */
227 static XErrorHandler Xsublim_Ss_Handler = NULL;
228 static int Xsublim_Ss_Status;
230 /* This was all basically swiped from driver/remote.c and util/vroot.h */
231 static int xsublim_Ss_Handler(Display* handle_Display,
232 XErrorEvent* handle_Error)
234 if (handle_Error->error_code == BadWindow)
236 Xsublim_Ss_Status = BadWindow;
239 if (Xsublim_Ss_Handler == NULL)
241 fprintf(stderr,"%s: ",progname);
244 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
246 static Window xsublim_Ss_GetWindow(Display* ss_Display)
249 Window win_RootReturn;
257 unsigned long prop_Count;
258 unsigned long prop_Bytes;
261 static Atom XA_SCREENSAVER_VERSION = -1;
262 static Atom __SWM_VROOT;
264 /* Assume bad things */
269 if (XA_SCREENSAVER_VERSION == -1)
271 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
272 "_SCREENSAVER_VERSION",FALSE);
273 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
276 /* Find a screensaver window */
277 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
278 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
279 &win_Child,&child_Count) != FALSE)
282 (win_Root == win_RootReturn) &&
284 (win_Child != NULL) &&
287 for (child_Index = 0;child_Index < child_Count;
290 XSync(ss_Display,FALSE);
291 Xsublim_Ss_Status = 0;
293 XSetErrorHandler(xsublim_Ss_Handler);
295 prop_Status = XGetWindowProperty(ss_Display,
296 win_Child[child_Index],XA_SCREENSAVER_VERSION,
297 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
298 &prop_Count,&prop_Bytes,
299 (unsigned char**)&prop_Value);
300 XSync(ss_Display,FALSE);
301 XSetErrorHandler(Xsublim_Ss_Handler);
302 if (prop_Value != NULL)
306 if (Xsublim_Ss_Status == BadWindow)
308 prop_Status = BadWindow;
310 if ((prop_Status == Success) &&
313 /* See if it's a virtual root */
316 XGetWindowProperty(ss_Display,
317 win_Child[child_Index],__SWM_VROOT,0,
318 1,FALSE,XA_WINDOW,&prop_Type,
319 &prop_Format,&prop_Count,&prop_Bytes,
320 (unsigned char**)&prop_Value);
321 if (prop_Value != NULL)
325 if ((prop_Status == Success) &&
329 win_Child[child_Index];
335 if (win_Child != NULL)
342 /* Main ==================================================================== */
343 static XErrorHandler Xsublim_Sh_Handler = NULL;
344 static int Xsublim_Sh_Status = 0;
346 static int xsublim_Sh_Handler(Display* handle_Display,
347 XErrorEvent* handle_Error)
349 if (handle_Error->error_code == BadMatch)
351 Xsublim_Sh_Status = BadMatch;
354 if (Xsublim_Sh_Handler == NULL)
356 fprintf(stderr,"%s: ",progname);
359 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
361 int main(int argc,char* argv[])
391 #if defined(SIGDANGER)
397 Display* disp_Display;
399 XWindowAttributes attr_Win;
400 XGCValues gc_ValFore;
401 XGCValues gc_ValBack;
404 XFontStruct* font_Font;
407 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
408 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
409 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
410 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
411 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
412 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
413 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
414 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
415 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
416 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
417 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
427 char* text_List[XSUBLIM_TEXT_COUNT];
428 int text_Used[XSUBLIM_TEXT_COUNT];
429 char text_Text[XSUBLIM_TEXT_LENGTH+1];
447 int text_OutlineIndex;
448 XImage* image_Image = NULL;
452 int image_Height = 0;
456 int arg_FlagScreensaver;
460 int arg_DelayPhraseMin;
461 int arg_DelayPhraseMax;
465 /* Set-up ---------------------------------------------------------- */
468 Xsublim_Sig_Last = -1;
469 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
471 signal(sig_Number,xsublim_Sig_Catch);
474 /* Randomize -- only need to do this here because this program
475 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
479 /* Handle all the X nonsense */
481 SgiUseSchemes("none");
483 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
487 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
496 int count = (sizeof(options)/sizeof(*options))-1;
497 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
498 fprintf (stderr, "Options include: ");
499 for (i = 0; i < count; i++)
501 char *sw = options [i].option;
502 Bool argp = (options [i].argKind == XrmoptionSepArg);
503 int size = strlen (sw) + (argp ? 6 : 0) + 2;
506 fprintf (stderr, "\n\t\t ");
510 fprintf (stderr, "%s", sw);
511 if (argp) fprintf (stderr, " <arg>");
512 if (i != count-1) fprintf (stderr, ", ");
514 fprintf (stderr, ".\n");
518 disp_Display = XtDisplay(app_App);
519 db = XtDatabase(disp_Display);
520 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
521 win_Root = RootWindowOfScreen(XtScreen(app_App));
522 XtDestroyWidget(app_App);
524 /* Get the arguments */
525 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
526 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
527 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
529 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
530 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
531 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
532 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
534 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
536 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
538 arg_DelayPhraseMax = arg_DelayPhraseMin;
541 /* Get the phrases */
545 memset(text_Used,0,sizeof(text_Used));
546 arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
547 if (arg_Source != NULL)
550 struct stat file_Stat;
552 file_Fs = fopen(arg_Source,"rb");
555 fprintf(stderr,"%s: Could not open '%s'\n",progname,
559 if (fstat(fileno(file_Fs),&file_Stat) != 0)
561 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
565 arg_Text = calloc(1,file_Stat.st_size+1);
566 if (arg_Text != NULL)
568 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
570 fprintf(stderr,"%s: Could not read '%s'\n",
571 progname,arg_Source);
579 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
581 if (arg_Source != NULL)
583 char* exe_Command = calloc(1,strlen(arg_Source)+10);
586 if (exe_Command == NULL)
589 "%s: Could not allocate space for '%s'\n",
590 progname,arg_Source);
593 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
595 exe_Fs = popen(exe_Command,"r");
598 fprintf(stderr,"%s: Could not run '%s'\n",
599 progname,arg_Source);
602 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
603 if (arg_Text != NULL)
605 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
609 "%s: Could not read output of '%s'\n",
610 progname,arg_Source);
614 strstr(arg_Text,": not found") ||
615 strstr(arg_Text,": Not found") ||
616 strstr(arg_Text,": command not found") ||
617 strstr(arg_Text,": Command not found"))
620 "%s: Could not find '%s'\n",
621 progname,arg_Source);
630 get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
631 if (arg_Text != NULL)
633 arg_Text = strdup(arg_Text);
637 if (arg_Text != NULL)
639 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
640 (text_Count < XSUBLIM_TEXT_COUNT))
643 text_List[text_Count] = text_Phrase;
646 text_List[text_Count] = NULL;
650 fprintf(stderr,"%s: No text to display\n",progname);
655 font_Font = XLoadQueryFont(disp_Display,
656 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
658 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
660 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
663 if (font_Font == NULL)
665 fprintf(stderr,"%s: Couldn't load a font\n",progname);
670 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
671 gc_ValFore.font = font_Font->fid;
672 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
673 disp_Display,attr_Win.colormap);
674 gc_ValFore.background = get_pixel_resource("background","Background",
675 disp_Display,attr_Win.colormap);
676 gc_ValFore.subwindow_mode = IncludeInferiors;
677 gc_GcFore = XCreateGC(disp_Display,win_Root,
678 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
679 gc_ValBack.font = font_Font->fid;
680 gc_ValBack.foreground = get_pixel_resource("background","Background",
681 disp_Display,attr_Win.colormap);
682 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
683 disp_Display,attr_Win.colormap);
684 gc_ValBack.subwindow_mode = IncludeInferiors;
685 gc_GcBack = XCreateGC(disp_Display,win_Root,
686 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
688 /* Loop ------------------------------------------------------------ */
689 while (Xsublim_Sig_Last == -1)
691 /* Once-per-phrase stuff ----------------------------------- */
693 /* If we're waiting for a screensaver... */
694 if (arg_FlagScreensaver != FALSE)
696 /* Find the screensaver's window */
697 win_Root = xsublim_Ss_GetWindow(disp_Display);
705 /* Pick the next phrase */
706 if (arg_FlagRandom != FALSE)
708 text_Item = random()%text_Count;
711 while (text_Used[text_Item] != FALSE)
715 if (text_Index == text_Count)
718 memset(text_Used,0,sizeof(text_Used));
720 if (text_List[text_Item] == NULL)
725 text_Used[text_Item] = TRUE;
726 strncpy(text_Text,text_List[text_Item],
727 XSUBLIM_TEXT_LENGTH);
728 text_Phrase = text_Text;
730 /* Run through the phrase */
731 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
732 (Xsublim_Sig_Last == -1))
736 /* Once-per-word stuff ----------------------------- */
738 /* Find the text's position */
739 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
740 text_Length = strlen(text_Word);
741 text_Width = XTextWidth(font_Font,text_Word,
742 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
743 text_Height = font_Font->ascent+font_Font->descent+1+
744 XSUBLIM_TEXT_OUTLINE*2;
745 if (arg_FlagCenter == FALSE)
747 text_X = random()%(attr_Win.width-text_Width);
748 text_Y = random()%(attr_Win.height-
753 text_X = (attr_Win.width/2)-(text_Width/2);
754 text_Y = (attr_Win.height/2)-(text_Height/2);
757 /* Find the image's position (and pad it out slightly,
758 otherwise bits of letter get left behind -- are
759 there boundry issues I don't know about?) */
762 image_Width = text_Width+32;
763 image_Height = text_Height;
772 if (image_X+image_Width > attr_Win.width)
774 image_Width = attr_Win.width-image_X;
776 if (image_Y+image_Height > attr_Win.height)
778 image_Height = attr_Win.height-image_Y;
781 /* Influence people for our own ends --------------- */
783 /* Grab the server -- we can't let anybody draw over
785 XSync(disp_Display,FALSE);
786 XGrabServer(disp_Display);
787 XSync(disp_Display,FALSE);
789 /* Set up an error handler that ignores BadMatches --
790 since the screensaver can take its window away at
791 any time, any call that uses it might choke */
792 Xsublim_Sh_Status = 0;
794 XSetErrorHandler(xsublim_Sh_Handler);
796 /* Save the current background */
797 image_Image = XGetImage(disp_Display,win_Root,image_X,
798 image_Y,image_Width,image_Height,~0L,ZPixmap);
800 /* If we've successfully saved the background... */
801 if (image_Image != NULL)
803 if (Xsublim_Sh_Status == 0)
805 /* Draw the outline */
806 if (arg_FlagOutline != FALSE)
808 for (text_OutlineIndex = 0;
810 text_OutlineIndex].outline_X
811 != 0;text_OutlineIndex++)
825 XSUBLIM_TEXT_OUTLINE,
831 XSUBLIM_TEXT_OUTLINE,
838 XDrawString(disp_Display,win_Root,
840 text_Y+(font_Font->ascent),text_Word,
843 if (Xsublim_Sh_Status == 0)
846 XSync(disp_Display,FALSE);
847 if (Xsublim_Sig_Last == -1)
849 usleep(arg_DelayShow);
852 /* Restore the background */
853 XPutImage(disp_Display,win_Root,
854 gc_GcFore,image_Image,0,0,image_X,
855 image_Y,image_Width,image_Height);
859 XDestroyImage(image_Image);
862 /* Restore the error handler, ungrab the server */
863 XSync(disp_Display,FALSE);
864 XSetErrorHandler(Xsublim_Sh_Handler);
865 XUngrabServer(disp_Display);
866 XSync(disp_Display,FALSE);
868 /* Pause between words */
869 if (Xsublim_Sig_Last == -1)
871 usleep(arg_DelayWord);
875 /* Pause between phrases */
876 if (Xsublim_Sig_Last == -1)
878 usleep(random()%(arg_DelayPhraseMax-
879 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
883 /* Exit ------------------------------------------------------------ */
884 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
886 signal(sig_Number,SIG_DFL);
888 kill(getpid(),Xsublim_Sig_Last);