1 /*****************************************************************************
3 * xsublim -- Submit. Conform. Obey. *
5 * Copyright (c) 1999 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 -delayShow ms Microsecs for display of each word
36 -delayWord ms Microsecs for blank between words
37 -delayPhraseMin ms Microsecs for min blank between phrases
38 -delayPhraseMax ms Microsecs for max blank between phrases
39 -random Show phrases in random order (Default)
40 -no-random Show phrases in listed order
41 -screensaver Wait for an active screensaver (Default)
42 -no-screensaver Draw over active windows
43 -outline Draw a contrasting outline around words (Default)
44 -no-outline Draw words without an outline
45 -center Draw words in the center of the screen (Default)
46 -no-center Draw words randomly around the screen
50 /* Changelog ******************************************************************
52 1.1.0 19991221 Added -file
53 1.0.1 19990716 Assume that XGetImage()/XDestroyImage() don't leak,
54 which they apparently don't. I have no idea how I
55 convinced myself that they did. Huh. (greg@eod.com)
56 1.0.0 19990716 Initial release
60 /* Defines *******************************************************************/
61 #define XSUBLIM_NAME "XSublim"
62 #define XSUBLIM_TEXT_COUNT 1000
63 #define XSUBLIM_TEXT_LENGTH 128
64 #define XSUBLIM_TEXT_OUTLINE 1
65 #define XSUBLIM_ARG_DELAYSHOW "delayShow"
66 #define XSUBLIM_ARG_DELAYWORD "delayWord"
67 #define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
68 #define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
69 #define XSUBLIM_ARG_RANDOM "random"
70 #define XSUBLIM_ARG_FILE "file"
71 #define XSUBLIM_ARG_SCREENSAVER "screensaver"
72 #define XSUBLIM_ARG_OUTLINE "outline"
73 #define XSUBLIM_ARG_CENTER "center"
74 #define XSUBLIM_ARG_FONT "font"
75 #define XSUBLIM_ARG_PHRASES "phrases"
82 /* Includes ******************************************************************/
88 #include <X11/Intrinsic.h>
89 #include <X11/IntrinsicP.h>
90 #include <X11/CoreP.h>
91 #include <X11/Shell.h>
92 #include <X11/StringDefs.h>
93 #include <X11/Xutil.h>
94 #include <X11/keysym.h>
95 #include <X11/Xatom.h>
98 #include <X11/Xproto.h>
100 #include <X11/SGIScheme.h>
104 #include "yarandom.h"
105 #include "resources.h"
108 /* Globals *******************************************************************/
112 char* progclass = XSUBLIM_NAME;
115 ".background: #000000",
116 ".foreground: #FFFFFF",
117 "*" XSUBLIM_ARG_PHRASES ":"
121 "OBEY. OBEY. OBEY.\\n"
130 "Despair quietly.\\n"
132 "You are being watched.\\n"
133 "You will be punished.\\n"
134 "You serve no purpose.\\n"
135 "Your contributions are ignored.\\n"
136 "They are laughing at you.\\n"
137 "They lied to you.\\n"
138 "They read your mail.\\n"
143 "You are a prisoner.\\n"
144 "You are helpless.\\n"
145 "You are diseased.\\n"
146 "Fear the unknown.\\n"
147 "Happiness follows obedience.\\n"
148 "Ignorance is strength.\\n"
150 "Freedom is slavery.\\n"
151 "Abandon all hope.\\n"
152 "You will be assimilated.\\n"
153 "Resistance is futile.\\n"
154 "Resistance is useless.\\n"
157 "What's that smell?\\n"
158 "All praise the company.\\n"
160 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
161 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
162 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
163 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
164 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
165 "*" XSUBLIM_ARG_RANDOM ": true",
166 "*" XSUBLIM_ARG_SCREENSAVER ": true",
167 "*" XSUBLIM_ARG_OUTLINE": true",
168 "*" XSUBLIM_ARG_CENTER": true",
171 XrmOptionDescRec options[] =
173 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
175 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
177 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
179 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
181 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
183 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
184 XrmoptionNoArg,"true"},
185 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
186 XrmoptionNoArg,"false"},
187 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
189 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
190 XrmoptionNoArg,"true"},
191 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
192 XrmoptionNoArg,"false"},
193 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
194 XrmoptionNoArg,"true"},
195 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
196 XrmoptionNoArg,"false"},
197 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
198 XrmoptionNoArg,"true"},
199 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
200 XrmoptionNoArg,"false"},
204 static int Xsublim_Sig_Last;
207 /* Functions *****************************************************************/
209 /* Defer signals to protect the server grab ================================ */
210 void xsublim_Sig_Catch(int sig_Number)
212 /* BSD needs this reset each time, and it shouldn't hurt anything
214 signal(sig_Number,xsublim_Sig_Catch);
215 Xsublim_Sig_Last = sig_Number;
218 /* Get the screensaver's window ============================================ */
219 static XErrorHandler Xsublim_Ss_Handler = NULL;
220 static int Xsublim_Ss_Status;
222 /* This was all basically swiped from driver/remote.c and util/vroot.h */
223 static int xsublim_Ss_Handler(Display* handle_Display,
224 XErrorEvent* handle_Error)
226 if (handle_Error->error_code == BadWindow)
228 Xsublim_Ss_Status = BadWindow;
231 if (Xsublim_Ss_Handler == NULL)
233 fprintf(stderr,"%x: ",progname);
236 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
238 static Window xsublim_Ss_GetWindow(Display* ss_Display)
241 Window win_RootReturn;
249 unsigned long prop_Count;
250 unsigned long prop_Bytes;
253 static Atom XA_SCREENSAVER_VERSION = -1;
254 static Atom __SWM_VROOT;
256 /* Assume bad things */
261 if (XA_SCREENSAVER_VERSION == -1)
263 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
264 "_SCREENSAVER_VERSION",FALSE);
265 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
268 /* Find a screensaver window */
269 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
270 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
271 &win_Child,&child_Count) != FALSE)
274 (win_Root == win_RootReturn) &&
276 (win_Child != NULL) &&
279 for (child_Index = 0;child_Index < child_Count;
282 XSync(ss_Display,FALSE);
283 Xsublim_Ss_Status = 0;
285 XSetErrorHandler(xsublim_Ss_Handler);
287 prop_Status = XGetWindowProperty(ss_Display,
288 win_Child[child_Index],XA_SCREENSAVER_VERSION,
289 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
290 &prop_Count,&prop_Bytes,
291 (unsigned char**)&prop_Value);
292 XSync(ss_Display,FALSE);
293 XSetErrorHandler(Xsublim_Ss_Handler);
294 if (prop_Value != NULL)
298 if (Xsublim_Ss_Status == BadWindow)
300 prop_Status = BadWindow;
302 if ((prop_Status == Success) &&
305 /* See if it's a virtual root */
308 XGetWindowProperty(ss_Display,
309 win_Child[child_Index],__SWM_VROOT,0,
310 1,FALSE,XA_WINDOW,&prop_Type,
311 &prop_Format,&prop_Count,&prop_Bytes,
312 (unsigned char**)&prop_Value);
313 if (prop_Value != NULL)
317 if ((prop_Status == Success) &&
321 win_Child[child_Index];
327 if (win_Child != NULL)
334 /* Main ==================================================================== */
335 static XErrorHandler Xsublim_Sh_Handler = NULL;
336 static int Xsublim_Sh_Status = 0;
338 static int xsublim_Sh_Handler(Display* handle_Display,
339 XErrorEvent* handle_Error)
341 if (handle_Error->error_code == BadMatch)
343 Xsublim_Sh_Status = BadMatch;
346 if (Xsublim_Sh_Handler == NULL)
348 fprintf(stderr,"%s: ",progname);
351 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
353 int main(int argc,char* argv[])
381 #if defined(SIGDANGER)
387 Display* disp_Display;
389 XWindowAttributes attr_Win;
390 XGCValues gc_ValFore;
391 XGCValues gc_ValBack;
394 XFontStruct* font_Font;
397 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
398 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
399 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
400 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
401 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
402 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
403 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
404 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
405 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
406 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
407 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
417 char* text_List[XSUBLIM_TEXT_COUNT];
418 int text_Used[XSUBLIM_TEXT_COUNT];
419 char text_Text[XSUBLIM_TEXT_LENGTH+1];
437 int text_OutlineIndex;
438 XImage* image_Image = NULL;
442 int image_Height = 0;
446 int arg_FlagScreensaver;
450 int arg_DelayPhraseMin;
451 int arg_DelayPhraseMax;
454 /* Set-up ---------------------------------------------------------- */
457 Xsublim_Sig_Last = -1;
458 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
460 signal(sig_Number,xsublim_Sig_Catch);
463 /* Randomize -- only need to do this here because this program
464 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
466 ya_rand_init ((int) time ((time_t *) 0));
468 /* Handle all the X nonsense */
470 SgiUseSchemes("none");
472 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
476 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
485 int count = (sizeof(options)/sizeof(*options))-1;
486 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
487 fprintf (stderr, "Options include: ");
488 for (i = 0; i < count; i++)
490 char *sw = options [i].option;
491 Bool argp = (options [i].argKind == XrmoptionSepArg);
492 int size = strlen (sw) + (argp ? 6 : 0) + 2;
495 fprintf (stderr, "\n\t\t ");
499 fprintf (stderr, "%s", sw);
500 if (argp) fprintf (stderr, " <arg>");
501 if (i != count-1) fprintf (stderr, ", ");
503 fprintf (stderr, ".\n");
507 disp_Display = XtDisplay(app_App);
508 db = XtDatabase(disp_Display);
509 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
510 win_Root = RootWindowOfScreen(XtScreen(app_App));
511 XtDestroyWidget(app_App);
513 /* Get the arguments */
514 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
515 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
516 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
518 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
519 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
520 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
521 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
523 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
525 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
527 arg_DelayPhraseMax = arg_DelayPhraseMin;
530 /* Get the phrases */
534 memset(text_Used,0,sizeof(text_Used));
535 arg_Text = get_string_resource(XSUBLIM_ARG_FILE,"Filename");
536 if (arg_Text != NULL)
539 struct stat file_Stat;
541 file_Fs = fopen(arg_Text,"rb");
544 fprintf(stderr,"%s: Could not open '%s'\n",progname,
548 if (fstat(fileno(file_Fs),&file_Stat) != 0)
550 fprintf(stderr,"%s: Could not stat '%s'\n",progname,
554 arg_Text = calloc(1,file_Stat.st_size+1);
555 if (arg_Text != NULL)
557 if (fread(arg_Text,file_Stat.st_size,1,file_Fs) != 1)
559 fprintf(stderr,"%s: Could not read '%s'\n",
568 arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
569 if (arg_Text != NULL)
571 arg_Text = strdup(arg_Text);
574 if (arg_Text != NULL)
576 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
577 (text_Count < XSUBLIM_TEXT_COUNT))
580 text_List[text_Count] = text_Phrase;
583 text_List[text_Count] = NULL;
587 fprintf(stderr,"%s: No text to display\n",progname);
592 font_Font = XLoadQueryFont(disp_Display,
593 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
595 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
597 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
600 if (font_Font == NULL)
602 fprintf(stderr,"%s: Couldn't load a font\n",progname);
607 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
608 gc_ValFore.font = font_Font->fid;
609 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
610 disp_Display,attr_Win.colormap);
611 gc_ValFore.background = get_pixel_resource("background","Background",
612 disp_Display,attr_Win.colormap);
613 gc_ValFore.subwindow_mode = IncludeInferiors;
614 gc_GcFore = XCreateGC(disp_Display,win_Root,
615 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
616 gc_ValBack.font = font_Font->fid;
617 gc_ValBack.foreground = get_pixel_resource("background","Background",
618 disp_Display,attr_Win.colormap);
619 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
620 disp_Display,attr_Win.colormap);
621 gc_ValBack.subwindow_mode = IncludeInferiors;
622 gc_GcBack = XCreateGC(disp_Display,win_Root,
623 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
625 /* Loop ------------------------------------------------------------ */
626 while (Xsublim_Sig_Last == -1)
628 /* Once-per-phrase stuff ----------------------------------- */
630 /* If we're waiting for a screensaver... */
631 if (arg_FlagScreensaver != FALSE)
633 /* Find the screensaver's window */
634 win_Root = xsublim_Ss_GetWindow(disp_Display);
642 /* Pick the next phrase */
643 if (arg_FlagRandom != FALSE)
645 text_Item = random()%text_Count;
648 while (text_Used[text_Item] != FALSE)
652 if (text_Index == text_Count)
655 memset(text_Used,0,sizeof(text_Used));
657 if (text_List[text_Item] == NULL)
662 text_Used[text_Item] = TRUE;
663 strncpy(text_Text,text_List[text_Item],
664 XSUBLIM_TEXT_LENGTH);
665 text_Phrase = text_Text;
667 /* Run through the phrase */
668 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
669 (Xsublim_Sig_Last == -1))
673 /* Once-per-word stuff ----------------------------- */
675 /* Find the text's position */
676 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
677 text_Length = strlen(text_Word);
678 text_Width = XTextWidth(font_Font,text_Word,
679 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
680 text_Height = font_Font->ascent+font_Font->descent+1+
681 XSUBLIM_TEXT_OUTLINE*2;
682 if (arg_FlagCenter == FALSE)
684 text_X = random()%(attr_Win.width-text_Width);
685 text_Y = random()%(attr_Win.height-
690 text_X = (attr_Win.width/2)-(text_Width/2);
691 text_Y = (attr_Win.height/2)-(text_Height/2);
694 /* Find the image's position (and pad it out slightly,
695 otherwise bits of letter get left behind -- are
696 there boundry issues I don't know about?) */
699 image_Width = text_Width+32;
700 image_Height = text_Height;
709 if (image_X+image_Width > attr_Win.width)
711 image_Width = attr_Win.width-image_X;
713 if (image_Y+image_Height > attr_Win.height)
715 image_Height = attr_Win.height-image_Y;
718 /* Influence people for our own ends --------------- */
720 /* Grab the server -- we can't let anybody draw over
722 XSync(disp_Display,FALSE);
723 XGrabServer(disp_Display);
724 XSync(disp_Display,FALSE);
726 /* Set up an error handler that ignores BadMatches --
727 since the screensaver can take its window away at
728 any time, any call that uses it might choke */
729 Xsublim_Sh_Status = 0;
731 XSetErrorHandler(xsublim_Sh_Handler);
733 /* Save the current background */
734 image_Image = XGetImage(disp_Display,win_Root,image_X,
735 image_Y,image_Width,image_Height,~0L,ZPixmap);
737 /* If we've successfully saved the background... */
738 if (image_Image != NULL)
740 if (Xsublim_Sh_Status == 0)
742 /* Draw the outline */
743 if (arg_FlagOutline != FALSE)
745 for (text_OutlineIndex = 0;
747 text_OutlineIndex].outline_X
748 != 0;text_OutlineIndex++)
762 XSUBLIM_TEXT_OUTLINE,
768 XSUBLIM_TEXT_OUTLINE,
775 XDrawString(disp_Display,win_Root,
777 text_Y+(font_Font->ascent),text_Word,
780 if (Xsublim_Sh_Status == 0)
783 XSync(disp_Display,FALSE);
784 if (Xsublim_Sig_Last == -1)
786 usleep(arg_DelayShow);
789 /* Restore the background */
790 XPutImage(disp_Display,win_Root,
791 gc_GcFore,image_Image,0,0,image_X,
792 image_Y,image_Width,image_Height);
796 XDestroyImage(image_Image);
799 /* Restore the error handler, ungrab the server */
800 XSync(disp_Display,FALSE);
801 XSetErrorHandler(Xsublim_Sh_Handler);
802 XUngrabServer(disp_Display);
803 XSync(disp_Display,FALSE);
805 /* Pause between words */
806 if (Xsublim_Sig_Last == -1)
808 usleep(arg_DelayWord);
812 /* Pause between phrases */
813 if (Xsublim_Sig_Last == -1)
815 usleep(random()%(arg_DelayPhraseMax-
816 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
820 /* Exit ------------------------------------------------------------ */
821 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
823 signal(sig_Number,SIG_DFL);
825 kill(getpid(),Xsublim_Sig_Last);