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 *******************************************************************/
115 char* progclass = XSUBLIM_NAME;
118 __extension__ /* don't warn about "string length is greater than the
119 length ISO C89 compilers are required to support"
120 in the following string constant... */
123 static char* defaults[] =
125 ".background: #000000",
126 ".foreground: #FFFFFF",
127 "*" XSUBLIM_ARG_PHRASES ":"
131 "OBEY. OBEY. OBEY.\\n"
140 "Despair quietly.\\n"
142 "You are being watched.\\n"
143 "You will be punished.\\n"
144 "You serve no purpose.\\n"
145 "Your contributions are ignored.\\n"
146 "They are laughing at you.\\n"
147 "They lied to you.\\n"
148 "They read your mail.\\n"
153 "You are a prisoner.\\n"
154 "You are helpless.\\n"
155 "You are diseased.\\n"
156 "Fear the unknown.\\n"
157 "Happiness follows obedience.\\n"
158 "Ignorance is strength.\\n"
160 "Freedom is slavery.\\n"
161 "Abandon all hope.\\n"
162 "You will be assimilated.\\n"
163 "Resistance is futile.\\n"
164 "Resistance is useless.\\n"
167 "What's that smell?\\n"
168 "All praise the company.\\n"
170 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
171 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
172 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
173 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
174 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
175 "*" XSUBLIM_ARG_RANDOM ": true",
176 "*" XSUBLIM_ARG_SCREENSAVER ": true",
177 "*" XSUBLIM_ARG_OUTLINE": true",
178 "*" XSUBLIM_ARG_CENTER": true",
182 static 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 static 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 /* This was all basically swiped from driver/remote.c and util/vroot.h */
234 static Window xsublim_Ss_GetWindow(Display* ss_Display)
236 Screen *s = DefaultScreenOfDisplay (ss_Display);
237 Window root = XRootWindowOfScreen (s);
238 Window vroot = VirtualRootWindowOfScreen (s);
245 /* Main ==================================================================== */
246 static XErrorHandler Xsublim_Sh_Handler = NULL;
247 static int Xsublim_Sh_Status = 0;
249 static int xsublim_Sh_Handler(Display* handle_Display,
250 XErrorEvent* handle_Error)
252 if (handle_Error->error_code == BadMatch)
254 Xsublim_Sh_Status = BadMatch;
257 if (Xsublim_Sh_Handler == NULL)
259 fprintf(stderr,"%s: ",progname);
262 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
266 int main(int argc,char* argv[])
297 #if defined(SIGDANGER)
305 XWindowAttributes attr_Win;
306 XGCValues gc_ValFore;
307 XGCValues gc_ValBack;
310 XFontStruct* font_Font;
313 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
314 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
315 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
316 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
317 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
318 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
319 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
320 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
321 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
331 char* text_List[XSUBLIM_TEXT_COUNT];
332 int text_Used[XSUBLIM_TEXT_COUNT];
333 char text_Text[XSUBLIM_TEXT_LENGTH+1];
351 int text_OutlineIndex;
352 XImage* image_Image = NULL;
356 int image_Height = 0;
360 int arg_FlagScreensaver;
364 int arg_DelayPhraseMin;
365 int arg_DelayPhraseMax;
369 /* Set-up ---------------------------------------------------------- */
372 Xsublim_Sig_Last = -1;
373 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
375 signal(sig_Number,xsublim_Sig_Catch);
378 /* Randomize -- only need to do this here because this program
379 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
383 /* Handle all the X nonsense */
385 SgiUseSchemes("none");
387 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
391 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
400 int count = (sizeof(options)/sizeof(*options))-1;
401 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
402 fprintf (stderr, "Options include: ");
403 for (i = 0; i < count; i++)
405 char *sw = options [i].option;
406 Bool argp = (options [i].argKind == XrmoptionSepArg);
407 int size = strlen (sw) + (argp ? 6 : 0) + 2;
410 fprintf (stderr, "\n\t\t ");
414 fprintf (stderr, "%s", sw);
415 if (argp) fprintf (stderr, " <arg>");
416 if (i != count-1) fprintf (stderr, ", ");
418 fprintf (stderr, ".\n");
422 dpy = XtDisplay(app_App);
423 XtGetApplicationNameAndClass(dpy,&progname,&progclass);
424 win_Root = RootWindowOfScreen(XtScreen(app_App));
425 XtDestroyWidget(app_App);
427 /* Get the arguments */
428 arg_FlagCenter = get_boolean_resource(dpy, XSUBLIM_ARG_CENTER,"Boolean");
429 arg_FlagOutline = get_boolean_resource(dpy, XSUBLIM_ARG_OUTLINE,"Boolean");
430 arg_FlagScreensaver = get_boolean_resource(dpy, XSUBLIM_ARG_SCREENSAVER,
432 arg_FlagRandom = get_boolean_resource(dpy, XSUBLIM_ARG_RANDOM,"Boolean");
433 arg_DelayShow = get_integer_resource(dpy, XSUBLIM_ARG_DELAYSHOW,"Integer");
434 arg_DelayWord = get_integer_resource(dpy, XSUBLIM_ARG_DELAYWORD,"Integer");
435 arg_DelayPhraseMin = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMIN,
437 arg_DelayPhraseMax = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMAX,
439 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
441 arg_DelayPhraseMax = arg_DelayPhraseMin;
444 /* Get the phrases */
448 memset(text_Used,0,sizeof(text_Used));
449 arg_Source = get_string_resource(dpy, XSUBLIM_ARG_FILE,"Filename");
450 if (arg_Source != NULL)
453 struct stat file_Stat;
455 file_Fs = fopen(arg_Source,"rb");
458 fprintf(stderr,"%s: Could not open '%s'\n",progname,
462 if (fstat(fileno(file_Fs),&file_Stat) != 0)
464 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
468 arg_Text = calloc(1,file_Stat.st_size+1);
469 if (arg_Text != NULL)
471 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
473 fprintf(stderr,"%s: Could not read '%s'\n",
474 progname,arg_Source);
482 arg_Source = get_string_resource(dpy, XSUBLIM_ARG_PROGRAM,
484 if (arg_Source != NULL)
486 char* exe_Command = calloc(1,strlen(arg_Source)+10);
489 if (exe_Command == NULL)
492 "%s: Could not allocate space for '%s'\n",
493 progname,arg_Source);
496 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
498 exe_Fs = popen(exe_Command,"r");
501 fprintf(stderr,"%s: Could not run '%s'\n",
502 progname,arg_Source);
505 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
506 if (arg_Text != NULL)
508 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
512 "%s: Could not read output of '%s'\n",
513 progname,arg_Source);
517 strstr(arg_Text,": not found") ||
518 strstr(arg_Text,": Not found") ||
519 strstr(arg_Text,": command not found") ||
520 strstr(arg_Text,": Command not found"))
523 "%s: Could not find '%s'\n",
524 progname,arg_Source);
533 get_string_resource(dpy, XSUBLIM_ARG_PHRASES,"Phrases");
534 if (arg_Text != NULL)
536 arg_Text = strdup(arg_Text);
540 if (arg_Text != NULL)
542 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
543 (text_Count < XSUBLIM_TEXT_COUNT))
546 text_List[text_Count] = text_Phrase;
549 text_List[text_Count] = NULL;
553 fprintf(stderr,"%s: No text to display\n",progname);
558 font_Font = XLoadQueryFont(dpy,
559 get_string_resource(dpy, XSUBLIM_ARG_FONT,"Font"));
561 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
563 font_Font = XLoadQueryFont(dpy,font_List[font_Index]);
566 if (font_Font == NULL)
568 fprintf(stderr,"%s: Couldn't load a font\n",progname);
573 XGetWindowAttributes(dpy,win_Root,&attr_Win);
574 gc_ValFore.font = font_Font->fid;
575 gc_ValFore.foreground = get_pixel_resource(dpy,
577 "foreground","Foreground");
578 gc_ValFore.background = get_pixel_resource(dpy,
580 "background","Background");
581 gc_ValFore.subwindow_mode = IncludeInferiors;
582 gc_GcFore = XCreateGC(dpy,win_Root,
583 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
584 gc_ValBack.font = font_Font->fid;
585 gc_ValBack.foreground = get_pixel_resource(dpy,
587 "background","Background");
588 gc_ValBack.background = get_pixel_resource(dpy,
590 "foreground","Foreground");
591 gc_ValBack.subwindow_mode = IncludeInferiors;
592 gc_GcBack = XCreateGC(dpy,win_Root,
593 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
595 /* Loop ------------------------------------------------------------ */
596 while (Xsublim_Sig_Last == -1)
598 /* Once-per-phrase stuff ----------------------------------- */
600 /* If we're waiting for a screensaver... */
601 if (arg_FlagScreensaver != FALSE)
603 /* Find the screensaver's window */
604 win_Root = xsublim_Ss_GetWindow(dpy);
612 /* Pick the next phrase */
613 if (arg_FlagRandom != FALSE)
615 text_Item = random()%text_Count;
618 while (text_Used[text_Item] != FALSE)
622 if (text_Index == text_Count)
625 memset(text_Used,0,sizeof(text_Used));
627 if (text_List[text_Item] == NULL)
632 text_Used[text_Item] = TRUE;
633 strncpy(text_Text,text_List[text_Item],
634 XSUBLIM_TEXT_LENGTH);
635 text_Phrase = text_Text;
637 /* Run through the phrase */
638 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
639 (Xsublim_Sig_Last == -1))
643 /* Once-per-word stuff ----------------------------- */
645 /* Find the text's position */
646 XGetWindowAttributes(dpy,win_Root,&attr_Win);
647 text_Length = strlen(text_Word);
648 text_Width = XTextWidth(font_Font,text_Word,
649 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
650 text_Height = font_Font->ascent+font_Font->descent+1+
651 XSUBLIM_TEXT_OUTLINE*2;
652 if (arg_FlagCenter == FALSE)
654 text_X = random()%(attr_Win.width-text_Width);
655 text_Y = random()%(attr_Win.height-
660 text_X = (attr_Win.width/2)-(text_Width/2);
661 text_Y = (attr_Win.height/2)-(text_Height/2);
664 /* Find the image's position (and pad it out slightly,
665 otherwise bits of letter get left behind -- are
666 there boundry issues I don't know about?) */
669 image_Width = text_Width+32;
670 image_Height = text_Height;
679 if (image_X+image_Width > attr_Win.width)
681 image_Width = attr_Win.width-image_X;
683 if (image_Y+image_Height > attr_Win.height)
685 image_Height = attr_Win.height-image_Y;
688 /* Influence people for our own ends --------------- */
690 /* Grab the server -- we can't let anybody draw over
696 /* Set up an error handler that ignores BadMatches --
697 since the screensaver can take its window away at
698 any time, any call that uses it might choke */
699 Xsublim_Sh_Status = 0;
701 XSetErrorHandler(xsublim_Sh_Handler);
703 /* Save the current background */
704 image_Image = XGetImage(dpy,win_Root,image_X,
705 image_Y,image_Width,image_Height,~0L,ZPixmap);
707 /* If we've successfully saved the background... */
708 if (image_Image != NULL)
710 if (Xsublim_Sh_Status == 0)
712 /* Draw the outline */
713 if (arg_FlagOutline != FALSE)
715 for (text_OutlineIndex = 0;
717 text_OutlineIndex].outline_X
718 != 0;text_OutlineIndex++)
732 XSUBLIM_TEXT_OUTLINE,
738 XSUBLIM_TEXT_OUTLINE,
745 XDrawString(dpy,win_Root,
747 text_Y+(font_Font->ascent),text_Word,
750 if (Xsublim_Sh_Status == 0)
754 if (Xsublim_Sig_Last == -1)
756 usleep(arg_DelayShow);
759 /* Restore the background */
760 XPutImage(dpy,win_Root,
761 gc_GcFore,image_Image,0,0,image_X,
762 image_Y,image_Width,image_Height);
766 XDestroyImage(image_Image);
769 /* Restore the error handler, ungrab the server */
771 XSetErrorHandler(Xsublim_Sh_Handler);
775 /* Pause between words */
776 if (Xsublim_Sig_Last == -1)
778 usleep(arg_DelayWord);
782 /* Pause between phrases */
783 if (Xsublim_Sig_Last == -1)
785 usleep(random()%(arg_DelayPhraseMax-
786 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
790 /* Exit ------------------------------------------------------------ */
791 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
793 signal(sig_Number,SIG_DFL);
795 kill(getpid(),Xsublim_Sig_Last);