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,"%x: ",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[])
389 #if defined(SIGDANGER)
395 Display* disp_Display;
397 XWindowAttributes attr_Win;
398 XGCValues gc_ValFore;
399 XGCValues gc_ValBack;
402 XFontStruct* font_Font;
405 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
406 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
407 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
408 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
409 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
410 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
411 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
412 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
413 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
414 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
415 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
425 char* text_List[XSUBLIM_TEXT_COUNT];
426 int text_Used[XSUBLIM_TEXT_COUNT];
427 char text_Text[XSUBLIM_TEXT_LENGTH+1];
445 int text_OutlineIndex;
446 XImage* image_Image = NULL;
450 int image_Height = 0;
454 int arg_FlagScreensaver;
458 int arg_DelayPhraseMin;
459 int arg_DelayPhraseMax;
463 /* Set-up ---------------------------------------------------------- */
466 Xsublim_Sig_Last = -1;
467 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
469 signal(sig_Number,xsublim_Sig_Catch);
472 /* Randomize -- only need to do this here because this program
473 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
477 /* Handle all the X nonsense */
479 SgiUseSchemes("none");
481 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
485 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
494 int count = (sizeof(options)/sizeof(*options))-1;
495 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
496 fprintf (stderr, "Options include: ");
497 for (i = 0; i < count; i++)
499 char *sw = options [i].option;
500 Bool argp = (options [i].argKind == XrmoptionSepArg);
501 int size = strlen (sw) + (argp ? 6 : 0) + 2;
504 fprintf (stderr, "\n\t\t ");
508 fprintf (stderr, "%s", sw);
509 if (argp) fprintf (stderr, " <arg>");
510 if (i != count-1) fprintf (stderr, ", ");
512 fprintf (stderr, ".\n");
516 disp_Display = XtDisplay(app_App);
517 db = XtDatabase(disp_Display);
518 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
519 win_Root = RootWindowOfScreen(XtScreen(app_App));
520 XtDestroyWidget(app_App);
522 /* Get the arguments */
523 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
524 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
525 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
527 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
528 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
529 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
530 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
532 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
534 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
536 arg_DelayPhraseMax = arg_DelayPhraseMin;
539 /* Get the phrases */
543 memset(text_Used,0,sizeof(text_Used));
544 arg_Source = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
545 if (arg_Source != NULL)
548 struct stat file_Stat;
550 file_Fs = fopen(arg_Source,"rb");
553 fprintf(stderr,"%s: Could not open '%s'\n",progname,
557 if (fstat(fileno(file_Fs),&file_Stat) != 0)
559 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
563 arg_Text = calloc(1,file_Stat.st_size+1);
564 if (arg_Text != NULL)
566 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
568 fprintf(stderr,"%s: Could not read '%s'\n",
569 progname,arg_Source);
577 arg_Source = get_string_resource(XSUBLIM_ARG_PROGRAM,
579 if (arg_Source != NULL)
581 char* exe_Command = calloc(1,strlen(arg_Source)+10);
584 if (exe_Command == NULL)
587 "%s: Could not allocate space for '%s'\n",
588 progname,arg_Source);
591 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
593 exe_Fs = popen(exe_Command,"r");
596 fprintf(stderr,"%s: Could not run '%s'\n",
597 progname,arg_Source);
600 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
601 if (arg_Text != NULL)
603 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
607 "%s: Could not read output of '%s'\n",
608 progname,arg_Source);
612 strstr(arg_Text,": not found") ||
613 strstr(arg_Text,": Not found") ||
614 strstr(arg_Text,": command not found") ||
615 strstr(arg_Text,": Command not found"))
618 "%s: Could not find '%s'\n",
619 progname,arg_Source);
628 get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
629 if (arg_Text != NULL)
631 arg_Text = strdup(arg_Text);
635 if (arg_Text != NULL)
637 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
638 (text_Count < XSUBLIM_TEXT_COUNT))
641 text_List[text_Count] = text_Phrase;
644 text_List[text_Count] = NULL;
648 fprintf(stderr,"%s: No text to display\n",progname);
653 font_Font = XLoadQueryFont(disp_Display,
654 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
656 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
658 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
661 if (font_Font == NULL)
663 fprintf(stderr,"%s: Couldn't load a font\n",progname);
668 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
669 gc_ValFore.font = font_Font->fid;
670 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
671 disp_Display,attr_Win.colormap);
672 gc_ValFore.background = get_pixel_resource("background","Background",
673 disp_Display,attr_Win.colormap);
674 gc_ValFore.subwindow_mode = IncludeInferiors;
675 gc_GcFore = XCreateGC(disp_Display,win_Root,
676 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
677 gc_ValBack.font = font_Font->fid;
678 gc_ValBack.foreground = get_pixel_resource("background","Background",
679 disp_Display,attr_Win.colormap);
680 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
681 disp_Display,attr_Win.colormap);
682 gc_ValBack.subwindow_mode = IncludeInferiors;
683 gc_GcBack = XCreateGC(disp_Display,win_Root,
684 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
686 /* Loop ------------------------------------------------------------ */
687 while (Xsublim_Sig_Last == -1)
689 /* Once-per-phrase stuff ----------------------------------- */
691 /* If we're waiting for a screensaver... */
692 if (arg_FlagScreensaver != FALSE)
694 /* Find the screensaver's window */
695 win_Root = xsublim_Ss_GetWindow(disp_Display);
703 /* Pick the next phrase */
704 if (arg_FlagRandom != FALSE)
706 text_Item = random()%text_Count;
709 while (text_Used[text_Item] != FALSE)
713 if (text_Index == text_Count)
716 memset(text_Used,0,sizeof(text_Used));
718 if (text_List[text_Item] == NULL)
723 text_Used[text_Item] = TRUE;
724 strncpy(text_Text,text_List[text_Item],
725 XSUBLIM_TEXT_LENGTH);
726 text_Phrase = text_Text;
728 /* Run through the phrase */
729 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
730 (Xsublim_Sig_Last == -1))
734 /* Once-per-word stuff ----------------------------- */
736 /* Find the text's position */
737 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
738 text_Length = strlen(text_Word);
739 text_Width = XTextWidth(font_Font,text_Word,
740 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
741 text_Height = font_Font->ascent+font_Font->descent+1+
742 XSUBLIM_TEXT_OUTLINE*2;
743 if (arg_FlagCenter == FALSE)
745 text_X = random()%(attr_Win.width-text_Width);
746 text_Y = random()%(attr_Win.height-
751 text_X = (attr_Win.width/2)-(text_Width/2);
752 text_Y = (attr_Win.height/2)-(text_Height/2);
755 /* Find the image's position (and pad it out slightly,
756 otherwise bits of letter get left behind -- are
757 there boundry issues I don't know about?) */
760 image_Width = text_Width+32;
761 image_Height = text_Height;
770 if (image_X+image_Width > attr_Win.width)
772 image_Width = attr_Win.width-image_X;
774 if (image_Y+image_Height > attr_Win.height)
776 image_Height = attr_Win.height-image_Y;
779 /* Influence people for our own ends --------------- */
781 /* Grab the server -- we can't let anybody draw over
783 XSync(disp_Display,FALSE);
784 XGrabServer(disp_Display);
785 XSync(disp_Display,FALSE);
787 /* Set up an error handler that ignores BadMatches --
788 since the screensaver can take its window away at
789 any time, any call that uses it might choke */
790 Xsublim_Sh_Status = 0;
792 XSetErrorHandler(xsublim_Sh_Handler);
794 /* Save the current background */
795 image_Image = XGetImage(disp_Display,win_Root,image_X,
796 image_Y,image_Width,image_Height,~0L,ZPixmap);
798 /* If we've successfully saved the background... */
799 if (image_Image != NULL)
801 if (Xsublim_Sh_Status == 0)
803 /* Draw the outline */
804 if (arg_FlagOutline != FALSE)
806 for (text_OutlineIndex = 0;
808 text_OutlineIndex].outline_X
809 != 0;text_OutlineIndex++)
823 XSUBLIM_TEXT_OUTLINE,
829 XSUBLIM_TEXT_OUTLINE,
836 XDrawString(disp_Display,win_Root,
838 text_Y+(font_Font->ascent),text_Word,
841 if (Xsublim_Sh_Status == 0)
844 XSync(disp_Display,FALSE);
845 if (Xsublim_Sig_Last == -1)
847 usleep(arg_DelayShow);
850 /* Restore the background */
851 XPutImage(disp_Display,win_Root,
852 gc_GcFore,image_Image,0,0,image_X,
853 image_Y,image_Width,image_Height);
857 XDestroyImage(image_Image);
860 /* Restore the error handler, ungrab the server */
861 XSync(disp_Display,FALSE);
862 XSetErrorHandler(Xsublim_Sh_Handler);
863 XUngrabServer(disp_Display);
864 XSync(disp_Display,FALSE);
866 /* Pause between words */
867 if (Xsublim_Sig_Last == -1)
869 usleep(arg_DelayWord);
873 /* Pause between phrases */
874 if (Xsublim_Sig_Last == -1)
876 usleep(random()%(arg_DelayPhraseMax-
877 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
881 /* Exit ------------------------------------------------------------ */
882 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
884 signal(sig_Number,SIG_DFL);
886 kill(getpid(),Xsublim_Sig_Last);