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 /* Get the screensaver's window ============================================ */
235 static XErrorHandler Xsublim_Ss_Handler = NULL;
236 static int Xsublim_Ss_Status;
238 /* This was all basically swiped from driver/remote.c and util/vroot.h */
239 static int xsublim_Ss_Handler(Display* handle_Display,
240 XErrorEvent* handle_Error)
242 if (handle_Error->error_code == BadWindow)
244 Xsublim_Ss_Status = BadWindow;
247 if (Xsublim_Ss_Handler == NULL)
249 fprintf(stderr,"%s: ",progname);
252 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
254 static Window xsublim_Ss_GetWindow(Display* ss_Display)
256 Screen *s = DefaultScreenOfDisplay (ss_Display);
257 Window root = XRootWindowOfScreen (s);
258 Window vroot = VirtualRootWindowOfScreen (s);
265 /* Main ==================================================================== */
266 static XErrorHandler Xsublim_Sh_Handler = NULL;
267 static int Xsublim_Sh_Status = 0;
269 static int xsublim_Sh_Handler(Display* handle_Display,
270 XErrorEvent* handle_Error)
272 if (handle_Error->error_code == BadMatch)
274 Xsublim_Sh_Status = BadMatch;
277 if (Xsublim_Sh_Handler == NULL)
279 fprintf(stderr,"%s: ",progname);
282 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
284 int main(int argc,char* argv[])
314 #if defined(SIGDANGER)
320 Display* disp_Display;
322 XWindowAttributes attr_Win;
323 XGCValues gc_ValFore;
324 XGCValues gc_ValBack;
327 XFontStruct* font_Font;
330 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
331 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
332 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
333 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
334 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
335 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
336 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
337 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
338 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
339 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
340 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
350 char* text_List[XSUBLIM_TEXT_COUNT];
351 int text_Used[XSUBLIM_TEXT_COUNT];
352 char text_Text[XSUBLIM_TEXT_LENGTH+1];
370 int text_OutlineIndex;
371 XImage* image_Image = NULL;
375 int image_Height = 0;
379 int arg_FlagScreensaver;
383 int arg_DelayPhraseMin;
384 int arg_DelayPhraseMax;
388 /* Set-up ---------------------------------------------------------- */
391 Xsublim_Sig_Last = -1;
392 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
394 signal(sig_Number,xsublim_Sig_Catch);
397 /* Randomize -- only need to do this here because this program
398 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
402 /* Handle all the X nonsense */
404 SgiUseSchemes("none");
406 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
410 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
419 int count = (sizeof(options)/sizeof(*options))-1;
420 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
421 fprintf (stderr, "Options include: ");
422 for (i = 0; i < count; i++)
424 char *sw = options [i].option;
425 Bool argp = (options [i].argKind == XrmoptionSepArg);
426 int size = strlen (sw) + (argp ? 6 : 0) + 2;
429 fprintf (stderr, "\n\t\t ");
433 fprintf (stderr, "%s", sw);
434 if (argp) fprintf (stderr, " <arg>");
435 if (i != count-1) fprintf (stderr, ", ");
437 fprintf (stderr, ".\n");
441 disp_Display = XtDisplay(app_App);
442 db = XtDatabase(disp_Display);
443 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
444 win_Root = RootWindowOfScreen(XtScreen(app_App));
445 XtDestroyWidget(app_App);
447 /* Get the arguments */
448 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
449 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
450 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
452 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
453 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
454 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
455 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
457 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
459 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
461 arg_DelayPhraseMax = arg_DelayPhraseMin;
464 /* Get the phrases */
468 memset(text_Used,0,sizeof(text_Used));
469 arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
470 if (arg_Source != NULL)
473 struct stat file_Stat;
475 file_Fs = fopen(arg_Source,"rb");
478 fprintf(stderr,"%s: Could not open '%s'\n",progname,
482 if (fstat(fileno(file_Fs),&file_Stat) != 0)
484 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
488 arg_Text = calloc(1,file_Stat.st_size+1);
489 if (arg_Text != NULL)
491 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
493 fprintf(stderr,"%s: Could not read '%s'\n",
494 progname,arg_Source);
502 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
504 if (arg_Source != NULL)
506 char* exe_Command = calloc(1,strlen(arg_Source)+10);
509 if (exe_Command == NULL)
512 "%s: Could not allocate space for '%s'\n",
513 progname,arg_Source);
516 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
518 exe_Fs = popen(exe_Command,"r");
521 fprintf(stderr,"%s: Could not run '%s'\n",
522 progname,arg_Source);
525 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
526 if (arg_Text != NULL)
528 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
532 "%s: Could not read output of '%s'\n",
533 progname,arg_Source);
537 strstr(arg_Text,": not found") ||
538 strstr(arg_Text,": Not found") ||
539 strstr(arg_Text,": command not found") ||
540 strstr(arg_Text,": Command not found"))
543 "%s: Could not find '%s'\n",
544 progname,arg_Source);
553 get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
554 if (arg_Text != NULL)
556 arg_Text = strdup(arg_Text);
560 if (arg_Text != NULL)
562 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
563 (text_Count < XSUBLIM_TEXT_COUNT))
566 text_List[text_Count] = text_Phrase;
569 text_List[text_Count] = NULL;
573 fprintf(stderr,"%s: No text to display\n",progname);
578 font_Font = XLoadQueryFont(disp_Display,
579 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
581 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
583 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
586 if (font_Font == NULL)
588 fprintf(stderr,"%s: Couldn't load a font\n",progname);
593 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
594 gc_ValFore.font = font_Font->fid;
595 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
596 disp_Display,attr_Win.colormap);
597 gc_ValFore.background = get_pixel_resource("background","Background",
598 disp_Display,attr_Win.colormap);
599 gc_ValFore.subwindow_mode = IncludeInferiors;
600 gc_GcFore = XCreateGC(disp_Display,win_Root,
601 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
602 gc_ValBack.font = font_Font->fid;
603 gc_ValBack.foreground = get_pixel_resource("background","Background",
604 disp_Display,attr_Win.colormap);
605 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
606 disp_Display,attr_Win.colormap);
607 gc_ValBack.subwindow_mode = IncludeInferiors;
608 gc_GcBack = XCreateGC(disp_Display,win_Root,
609 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
611 /* Loop ------------------------------------------------------------ */
612 while (Xsublim_Sig_Last == -1)
614 /* Once-per-phrase stuff ----------------------------------- */
616 /* If we're waiting for a screensaver... */
617 if (arg_FlagScreensaver != FALSE)
619 /* Find the screensaver's window */
620 win_Root = xsublim_Ss_GetWindow(disp_Display);
628 /* Pick the next phrase */
629 if (arg_FlagRandom != FALSE)
631 text_Item = random()%text_Count;
634 while (text_Used[text_Item] != FALSE)
638 if (text_Index == text_Count)
641 memset(text_Used,0,sizeof(text_Used));
643 if (text_List[text_Item] == NULL)
648 text_Used[text_Item] = TRUE;
649 strncpy(text_Text,text_List[text_Item],
650 XSUBLIM_TEXT_LENGTH);
651 text_Phrase = text_Text;
653 /* Run through the phrase */
654 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
655 (Xsublim_Sig_Last == -1))
659 /* Once-per-word stuff ----------------------------- */
661 /* Find the text's position */
662 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
663 text_Length = strlen(text_Word);
664 text_Width = XTextWidth(font_Font,text_Word,
665 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
666 text_Height = font_Font->ascent+font_Font->descent+1+
667 XSUBLIM_TEXT_OUTLINE*2;
668 if (arg_FlagCenter == FALSE)
670 text_X = random()%(attr_Win.width-text_Width);
671 text_Y = random()%(attr_Win.height-
676 text_X = (attr_Win.width/2)-(text_Width/2);
677 text_Y = (attr_Win.height/2)-(text_Height/2);
680 /* Find the image's position (and pad it out slightly,
681 otherwise bits of letter get left behind -- are
682 there boundry issues I don't know about?) */
685 image_Width = text_Width+32;
686 image_Height = text_Height;
695 if (image_X+image_Width > attr_Win.width)
697 image_Width = attr_Win.width-image_X;
699 if (image_Y+image_Height > attr_Win.height)
701 image_Height = attr_Win.height-image_Y;
704 /* Influence people for our own ends --------------- */
706 /* Grab the server -- we can't let anybody draw over
708 XSync(disp_Display,FALSE);
709 XGrabServer(disp_Display);
710 XSync(disp_Display,FALSE);
712 /* Set up an error handler that ignores BadMatches --
713 since the screensaver can take its window away at
714 any time, any call that uses it might choke */
715 Xsublim_Sh_Status = 0;
717 XSetErrorHandler(xsublim_Sh_Handler);
719 /* Save the current background */
720 image_Image = XGetImage(disp_Display,win_Root,image_X,
721 image_Y,image_Width,image_Height,~0L,ZPixmap);
723 /* If we've successfully saved the background... */
724 if (image_Image != NULL)
726 if (Xsublim_Sh_Status == 0)
728 /* Draw the outline */
729 if (arg_FlagOutline != FALSE)
731 for (text_OutlineIndex = 0;
733 text_OutlineIndex].outline_X
734 != 0;text_OutlineIndex++)
748 XSUBLIM_TEXT_OUTLINE,
754 XSUBLIM_TEXT_OUTLINE,
761 XDrawString(disp_Display,win_Root,
763 text_Y+(font_Font->ascent),text_Word,
766 if (Xsublim_Sh_Status == 0)
769 XSync(disp_Display,FALSE);
770 if (Xsublim_Sig_Last == -1)
772 usleep(arg_DelayShow);
775 /* Restore the background */
776 XPutImage(disp_Display,win_Root,
777 gc_GcFore,image_Image,0,0,image_X,
778 image_Y,image_Width,image_Height);
782 XDestroyImage(image_Image);
785 /* Restore the error handler, ungrab the server */
786 XSync(disp_Display,FALSE);
787 XSetErrorHandler(Xsublim_Sh_Handler);
788 XUngrabServer(disp_Display);
789 XSync(disp_Display,FALSE);
791 /* Pause between words */
792 if (Xsublim_Sig_Last == -1)
794 usleep(arg_DelayWord);
798 /* Pause between phrases */
799 if (Xsublim_Sig_Last == -1)
801 usleep(random()%(arg_DelayPhraseMax-
802 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
806 /* Exit ------------------------------------------------------------ */
807 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
809 signal(sig_Number,SIG_DFL);
811 kill(getpid(),Xsublim_Sig_Last);