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 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
317 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
318 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
319 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
320 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
321 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
322 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
323 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
333 char* text_List[XSUBLIM_TEXT_COUNT];
334 int text_Used[XSUBLIM_TEXT_COUNT];
335 char text_Text[XSUBLIM_TEXT_LENGTH+1];
353 int text_OutlineIndex;
354 XImage* image_Image = NULL;
358 int image_Height = 0;
362 int arg_FlagScreensaver;
366 int arg_DelayPhraseMin;
367 int arg_DelayPhraseMax;
371 /* Set-up ---------------------------------------------------------- */
374 Xsublim_Sig_Last = -1;
375 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
377 signal(sig_Number,xsublim_Sig_Catch);
380 /* Randomize -- only need to do this here because this program
381 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
385 /* Handle all the X nonsense */
387 SgiUseSchemes("none");
389 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
393 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
402 int count = (sizeof(options)/sizeof(*options))-1;
403 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
404 fprintf (stderr, "Options include: ");
405 for (i = 0; i < count; i++)
407 char *sw = options [i].option;
408 Bool argp = (options [i].argKind == XrmoptionSepArg);
409 int size = strlen (sw) + (argp ? 6 : 0) + 2;
412 fprintf (stderr, "\n\t\t ");
416 fprintf (stderr, "%s", sw);
417 if (argp) fprintf (stderr, " <arg>");
418 if (i != count-1) fprintf (stderr, ", ");
420 fprintf (stderr, ".\n");
424 dpy = XtDisplay(app_App);
425 XtGetApplicationNameAndClass(dpy,&progname,&progclass);
426 win_Root = RootWindowOfScreen(XtScreen(app_App));
427 XtDestroyWidget(app_App);
429 /* Get the arguments */
430 arg_FlagCenter = get_boolean_resource(dpy, XSUBLIM_ARG_CENTER,"Boolean");
431 arg_FlagOutline = get_boolean_resource(dpy, XSUBLIM_ARG_OUTLINE,"Boolean");
432 arg_FlagScreensaver = get_boolean_resource(dpy, XSUBLIM_ARG_SCREENSAVER,
434 arg_FlagRandom = get_boolean_resource(dpy, XSUBLIM_ARG_RANDOM,"Boolean");
435 arg_DelayShow = get_integer_resource(dpy, XSUBLIM_ARG_DELAYSHOW,"Integer");
436 arg_DelayWord = get_integer_resource(dpy, XSUBLIM_ARG_DELAYWORD,"Integer");
437 arg_DelayPhraseMin = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMIN,
439 arg_DelayPhraseMax = get_integer_resource(dpy, XSUBLIM_ARG_DELAYPHRASEMAX,
441 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
443 arg_DelayPhraseMax = arg_DelayPhraseMin;
446 /* Get the phrases */
450 memset(text_Used,0,sizeof(text_Used));
451 arg_Source = get_string_resource(dpy, XSUBLIM_ARG_FILE,"Filename");
452 if (arg_Source != NULL)
455 struct stat file_Stat;
457 file_Fs = fopen(arg_Source,"rb");
460 fprintf(stderr,"%s: Could not open '%s'\n",progname,
464 if (fstat(fileno(file_Fs),&file_Stat) != 0)
466 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
470 arg_Text = calloc(1,file_Stat.st_size+1);
471 if (arg_Text != NULL)
473 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
475 fprintf(stderr,"%s: Could not read '%s'\n",
476 progname,arg_Source);
484 arg_Source = get_string_resource(dpy, XSUBLIM_ARG_PROGRAM,
486 if (arg_Source != NULL)
488 char* exe_Command = calloc(1,strlen(arg_Source)+10);
491 if (exe_Command == NULL)
494 "%s: Could not allocate space for '%s'\n",
495 progname,arg_Source);
498 sprintf(exe_Command,"( %s ) 2>&1",arg_Source);
500 exe_Fs = popen(exe_Command,"r");
503 fprintf(stderr,"%s: Could not run '%s'\n",
504 progname,arg_Source);
507 arg_Text = calloc(1,XSUBLIM_PROGRAM_SIZE);
508 if (arg_Text != NULL)
510 if (fread(arg_Text,1,XSUBLIM_PROGRAM_SIZE,
514 "%s: Could not read output of '%s'\n",
515 progname,arg_Source);
519 strstr(arg_Text,": not found") ||
520 strstr(arg_Text,": Not found") ||
521 strstr(arg_Text,": command not found") ||
522 strstr(arg_Text,": Command not found"))
525 "%s: Could not find '%s'\n",
526 progname,arg_Source);
535 get_string_resource(dpy, XSUBLIM_ARG_PHRASES,"Phrases");
536 if (arg_Text != NULL)
538 arg_Text = strdup(arg_Text);
542 if (arg_Text != NULL)
544 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
545 (text_Count < XSUBLIM_TEXT_COUNT))
548 text_List[text_Count] = text_Phrase;
551 text_List[text_Count] = NULL;
555 fprintf(stderr,"%s: No text to display\n",progname);
560 font_Font = XLoadQueryFont(dpy,
561 get_string_resource(dpy, XSUBLIM_ARG_FONT,"Font"));
563 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
565 font_Font = XLoadQueryFont(dpy,font_List[font_Index]);
568 if (font_Font == NULL)
570 fprintf(stderr,"%s: Couldn't load a font\n",progname);
575 XGetWindowAttributes(dpy,win_Root,&attr_Win);
576 gc_ValFore.font = font_Font->fid;
577 gc_ValFore.foreground = get_pixel_resource(dpy,
579 "foreground","Foreground");
580 gc_ValFore.background = get_pixel_resource(dpy,
582 "background","Background");
583 gc_ValFore.subwindow_mode = IncludeInferiors;
584 gc_GcFore = XCreateGC(dpy,win_Root,
585 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
586 gc_ValBack.font = font_Font->fid;
587 gc_ValBack.foreground = get_pixel_resource(dpy,
589 "background","Background");
590 gc_ValBack.background = get_pixel_resource(dpy,
592 "foreground","Foreground");
593 gc_ValBack.subwindow_mode = IncludeInferiors;
594 gc_GcBack = XCreateGC(dpy,win_Root,
595 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
597 /* Loop ------------------------------------------------------------ */
598 while (Xsublim_Sig_Last == -1)
600 /* Once-per-phrase stuff ----------------------------------- */
602 /* If we're waiting for a screensaver... */
603 if (arg_FlagScreensaver != FALSE)
605 /* Find the screensaver's window */
606 win_Root = xsublim_Ss_GetWindow(dpy);
614 /* Pick the next phrase */
615 if (arg_FlagRandom != FALSE)
617 text_Item = random()%text_Count;
620 while (text_Used[text_Item] != FALSE)
624 if (text_Index == text_Count)
627 memset(text_Used,0,sizeof(text_Used));
629 if (text_List[text_Item] == NULL)
634 text_Used[text_Item] = TRUE;
635 strncpy(text_Text,text_List[text_Item],
636 XSUBLIM_TEXT_LENGTH);
637 text_Phrase = text_Text;
639 /* Run through the phrase */
640 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
641 (Xsublim_Sig_Last == -1))
645 /* Once-per-word stuff ----------------------------- */
647 /* Find the text's position */
648 XGetWindowAttributes(dpy,win_Root,&attr_Win);
649 text_Length = strlen(text_Word);
650 text_Width = XTextWidth(font_Font,text_Word,
651 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
652 text_Height = font_Font->ascent+font_Font->descent+1+
653 XSUBLIM_TEXT_OUTLINE*2;
654 if (arg_FlagCenter == FALSE)
656 text_X = random()%(attr_Win.width-text_Width);
657 text_Y = random()%(attr_Win.height-
662 text_X = (attr_Win.width/2)-(text_Width/2);
663 text_Y = (attr_Win.height/2)-(text_Height/2);
666 /* Find the image's position (and pad it out slightly,
667 otherwise bits of letter get left behind -- are
668 there boundry issues I don't know about?) */
671 image_Width = text_Width+32;
672 image_Height = text_Height;
681 if (image_X+image_Width > attr_Win.width)
683 image_Width = attr_Win.width-image_X;
685 if (image_Y+image_Height > attr_Win.height)
687 image_Height = attr_Win.height-image_Y;
690 /* Influence people for our own ends --------------- */
692 /* Grab the server -- we can't let anybody draw over
698 /* Set up an error handler that ignores BadMatches --
699 since the screensaver can take its window away at
700 any time, any call that uses it might choke */
701 Xsublim_Sh_Status = 0;
703 XSetErrorHandler(xsublim_Sh_Handler);
705 /* Save the current background */
706 image_Image = XGetImage(dpy,win_Root,image_X,
707 image_Y,image_Width,image_Height,~0L,ZPixmap);
709 /* If we've successfully saved the background... */
710 if (image_Image != NULL)
712 if (Xsublim_Sh_Status == 0)
714 /* Draw the outline */
715 if (arg_FlagOutline != FALSE)
717 for (text_OutlineIndex = 0;
719 text_OutlineIndex].outline_X
720 != 0;text_OutlineIndex++)
734 XSUBLIM_TEXT_OUTLINE,
740 XSUBLIM_TEXT_OUTLINE,
747 XDrawString(dpy,win_Root,
749 text_Y+(font_Font->ascent),text_Word,
752 if (Xsublim_Sh_Status == 0)
756 if (Xsublim_Sig_Last == -1)
758 usleep(arg_DelayShow);
761 /* Restore the background */
762 XPutImage(dpy,win_Root,
763 gc_GcFore,image_Image,0,0,image_X,
764 image_Y,image_Width,image_Height);
768 XDestroyImage(image_Image);
771 /* Restore the error handler, ungrab the server */
773 XSetErrorHandler(Xsublim_Sh_Handler);
777 /* Pause between words */
778 if (Xsublim_Sig_Last == -1)
780 usleep(arg_DelayWord);
784 /* Pause between phrases */
785 if (Xsublim_Sig_Last == -1)
787 usleep(random()%(arg_DelayPhraseMax-
788 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
792 /* Exit ------------------------------------------------------------ */
793 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
795 signal(sig_Number,SIG_DFL);
797 kill(getpid(),Xsublim_Sig_Last);