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"
110 #include "font-retry.h"
114 /* 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... */
124 static char* defaults[] =
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",
183 static 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 static 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);
267 int main(int argc,char* argv[])
298 #if defined(SIGDANGER)
306 XWindowAttributes attr_Win;
307 XGCValues gc_ValFore;
308 XGCValues gc_ValBack;
311 XFontStruct* font_Font;
314 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
315 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
316 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
317 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
318 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
319 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
320 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
321 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
322 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
332 char* text_List[XSUBLIM_TEXT_COUNT];
333 int text_Used[XSUBLIM_TEXT_COUNT];
334 char text_Text[XSUBLIM_TEXT_LENGTH+1];
352 int text_OutlineIndex;
353 XImage* image_Image = NULL;
357 int image_Height = 0;
361 int arg_FlagScreensaver;
365 int arg_DelayPhraseMin;
366 int arg_DelayPhraseMax;
370 /* Set-up ---------------------------------------------------------- */
373 Xsublim_Sig_Last = -1;
374 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
376 signal(sig_Number,xsublim_Sig_Catch);
379 /* Randomize -- only need to do this here because this program
380 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
384 /* Handle all the X nonsense */
386 SgiUseSchemes("none");
388 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
392 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
401 int count = (sizeof(options)/sizeof(*options))-1;
402 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
403 fprintf (stderr, "Options include: ");
404 for (i = 0; i < count; i++)
406 char *sw = options [i].option;
407 Bool argp = (options [i].argKind == XrmoptionSepArg);
408 int size = strlen (sw) + (argp ? 6 : 0) + 2;
411 fprintf (stderr, "\n\t\t ");
415 fprintf (stderr, "%s", sw);
416 if (argp) fprintf (stderr, " <arg>");
417 if (i != count-1) fprintf (stderr, ", ");
419 fprintf (stderr, ".\n");
423 dpy = XtDisplay(app_App);
424 XtGetApplicationNameAndClass(dpy,&progname,&progclass);
425 win_Root = RootWindowOfScreen(XtScreen(app_App));
426 XtDestroyWidget(app_App);
428 /* Get the arguments */
429 arg_FlagCenter = get_boolean_resource(dpy, XSUBLIM_ARG_CENTER,"Boolean");
430 arg_FlagOutline = get_boolean_resource(dpy, XSUBLIM_ARG_OUTLINE,"Boolean");
431 arg_FlagScreensaver = get_boolean_resource(dpy, XSUBLIM_ARG_SCREENSAVER,
433 arg_FlagRandom = get_boolean_resource(dpy, XSUBLIM_ARG_RANDOM,"Boolean");
434 arg_DelayShow = get_integer_resource(dpy, XSUBLIM_ARG_DELAYSHOW,"Integer");
435 arg_DelayWord = get_integer_resource(dpy, XSUBLIM_ARG_DELAYWORD,"Integer");
436 arg_DelayPhraseMin = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMIN,
438 arg_DelayPhraseMax = get_integer_resource(dpy, 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(dpy, 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(dpy, 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(dpy, 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 = load_font_retry(dpy,
560 get_string_resource(dpy, XSUBLIM_ARG_FONT,"Font"));
562 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
564 font_Font = load_font_retry(dpy,font_List[font_Index]);
567 if (font_Font == NULL)
569 fprintf(stderr,"%s: Couldn't load a font\n",progname);
574 XGetWindowAttributes(dpy,win_Root,&attr_Win);
575 gc_ValFore.font = font_Font->fid;
576 gc_ValFore.foreground = get_pixel_resource(dpy,
578 "foreground","Foreground");
579 gc_ValFore.background = get_pixel_resource(dpy,
581 "background","Background");
582 gc_ValFore.subwindow_mode = IncludeInferiors;
583 gc_GcFore = XCreateGC(dpy,win_Root,
584 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
585 gc_ValBack.font = font_Font->fid;
586 gc_ValBack.foreground = get_pixel_resource(dpy,
588 "background","Background");
589 gc_ValBack.background = get_pixel_resource(dpy,
591 "foreground","Foreground");
592 gc_ValBack.subwindow_mode = IncludeInferiors;
593 gc_GcBack = XCreateGC(dpy,win_Root,
594 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
596 /* Loop ------------------------------------------------------------ */
597 while (Xsublim_Sig_Last == -1)
599 /* Once-per-phrase stuff ----------------------------------- */
601 /* If we're waiting for a screensaver... */
602 if (arg_FlagScreensaver != FALSE)
604 /* Find the screensaver's window */
605 win_Root = xsublim_Ss_GetWindow(dpy);
613 /* Pick the next phrase */
614 if (arg_FlagRandom != FALSE)
616 text_Item = random()%text_Count;
619 while (text_Used[text_Item] != FALSE)
623 if (text_Index == text_Count)
626 memset(text_Used,0,sizeof(text_Used));
628 if (text_List[text_Item] == NULL)
633 text_Used[text_Item] = TRUE;
634 strncpy(text_Text,text_List[text_Item],
635 XSUBLIM_TEXT_LENGTH);
636 text_Phrase = text_Text;
638 /* Run through the phrase */
639 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
640 (Xsublim_Sig_Last == -1))
644 /* Once-per-word stuff ----------------------------- */
646 /* Find the text's position */
647 XGetWindowAttributes(dpy,win_Root,&attr_Win);
648 text_Length = strlen(text_Word);
649 text_Width = XTextWidth(font_Font,text_Word,
650 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
651 text_Height = font_Font->ascent+font_Font->descent+1+
652 XSUBLIM_TEXT_OUTLINE*2;
653 if (arg_FlagCenter == FALSE)
655 text_X = random()%(attr_Win.width-text_Width);
656 text_Y = random()%(attr_Win.height-
661 text_X = (attr_Win.width/2)-(text_Width/2);
662 text_Y = (attr_Win.height/2)-(text_Height/2);
665 /* Find the image's position (and pad it out slightly,
666 otherwise bits of letter get left behind -- are
667 there boundry issues I don't know about?) */
670 image_Width = text_Width+32;
671 image_Height = text_Height;
680 if (image_X+image_Width > attr_Win.width)
682 image_Width = attr_Win.width-image_X;
684 if (image_Y+image_Height > attr_Win.height)
686 image_Height = attr_Win.height-image_Y;
689 /* Influence people for our own ends --------------- */
691 /* Grab the server -- we can't let anybody draw over
697 /* Set up an error handler that ignores BadMatches --
698 since the screensaver can take its window away at
699 any time, any call that uses it might choke */
700 Xsublim_Sh_Status = 0;
702 XSetErrorHandler(xsublim_Sh_Handler);
704 /* Save the current background */
705 image_Image = XGetImage(dpy,win_Root,image_X,
706 image_Y,image_Width,image_Height,~0L,ZPixmap);
708 /* If we've successfully saved the background... */
709 if (image_Image != NULL)
711 if (Xsublim_Sh_Status == 0)
713 /* Draw the outline */
714 if (arg_FlagOutline != FALSE)
716 for (text_OutlineIndex = 0;
718 text_OutlineIndex].outline_X
719 != 0;text_OutlineIndex++)
733 XSUBLIM_TEXT_OUTLINE,
739 XSUBLIM_TEXT_OUTLINE,
746 XDrawString(dpy,win_Root,
748 text_Y+(font_Font->ascent),text_Word,
751 if (Xsublim_Sh_Status == 0)
755 if (Xsublim_Sig_Last == -1)
757 usleep(arg_DelayShow);
760 /* Restore the background */
761 XPutImage(dpy,win_Root,
762 gc_GcFore,image_Image,0,0,image_X,
763 image_Y,image_Width,image_Height);
767 XDestroyImage(image_Image);
770 /* Restore the error handler, ungrab the server */
772 XSetErrorHandler(Xsublim_Sh_Handler);
776 /* Pause between words */
777 if (Xsublim_Sig_Last == -1)
779 usleep(arg_DelayWord);
783 /* Pause between phrases */
784 if (Xsublim_Sig_Last == -1)
786 usleep(random()%(arg_DelayPhraseMax-
787 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
791 /* Exit ------------------------------------------------------------ */
792 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
794 signal(sig_Number,SIG_DFL);
796 kill(getpid(),Xsublim_Sig_Last);