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 /* Changelog ******************************************************************
33 1.0.1 19990716 Assume that XGetImage()/XDestroyImage() don't leak,
34 which they apparently don't. I have no idea how I
35 convinced myself that they did. Huh. (greg@eod.com)
36 1.0.0 19990716 Initial release
40 /* Arguments ******************************************************************
42 -font font Font to use
43 -delayShow ms Microsecs for display of each word
44 -delayWord ms Microsecs for blank between words
45 -delayPhraseMin ms Microsecs for min blank between phrases
46 -delayPhraseMax ms Microsecs for max blank between phrases
47 -random Show phrases in random order (Default)
48 -no-random Show phrases in listed order
49 -screensaver Wait for an active screensaver (Default)
50 -no-screensaver Draw over active windows
51 -outline Draw a contrasting outline around words (Default)
52 -no-outline Draw words without an outline
53 -center Draw words in the center of the screen (Default)
54 -no-center Draw words randomly around the screen
58 /* Defines *******************************************************************/
59 #define XSUBLIM_NAME "XSublim"
60 #define XSUBLIM_TEXT_COUNT 1000
61 #define XSUBLIM_TEXT_LENGTH 128
62 #define XSUBLIM_TEXT_OUTLINE 1
63 #define XSUBLIM_ARG_DELAYSHOW "delayShow"
64 #define XSUBLIM_ARG_DELAYWORD "delayWord"
65 #define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
66 #define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
67 #define XSUBLIM_ARG_RANDOM "random"
68 #define XSUBLIM_ARG_FILE "file"
69 #define XSUBLIM_ARG_SCREENSAVER "screensaver"
70 #define XSUBLIM_ARG_OUTLINE "outline"
71 #define XSUBLIM_ARG_CENTER "center"
72 #define XSUBLIM_ARG_FONT "font"
73 #define XSUBLIM_ARG_PHRASES "phrases"
80 /* Includes ******************************************************************/
85 #include <X11/Intrinsic.h>
86 #include <X11/IntrinsicP.h>
87 #include <X11/CoreP.h>
88 #include <X11/Shell.h>
89 #include <X11/StringDefs.h>
90 #include <X11/Xutil.h>
91 #include <X11/keysym.h>
92 #include <X11/Xatom.h>
95 #include <X11/Xproto.h>
97 #include <X11/SGIScheme.h>
100 #include "yarandom.h"
102 #include "resources.h"
105 /* Globals *******************************************************************/
109 char* progclass = XSUBLIM_NAME;
112 ".background: #000000",
113 ".foreground: #FFFFFF",
114 "*" XSUBLIM_ARG_PHRASES ":"
118 "OBEY. OBEY. OBEY.\\n"
127 "Despair quietly.\\n"
129 "You are being watched.\\n"
130 "You will be punished.\\n"
131 "You serve no purpose.\\n"
132 "Your contributions are ignored.\\n"
133 "They are laughing at you.\\n"
134 "They lied to you.\\n"
135 "They read your mail.\\n"
140 "You are a prisoner.\\n"
141 "You are helpless.\\n"
142 "You are diseased.\\n"
143 "Fear the unknown.\\n"
144 "Happiness follows obedience.\\n"
145 "Ignorance is strength.\\n"
147 "Freedom is slavery.\\n"
148 "Abandon all hope.\\n"
149 "You will be assimilated.\\n"
150 "Resistance is futile.\\n"
151 "Resistance is useless.\\n"
154 "What's that smell?\\n"
155 "All praise the company.\\n"
157 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
158 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
159 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
160 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
161 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
162 "*" XSUBLIM_ARG_RANDOM ": true",
163 "*" XSUBLIM_ARG_SCREENSAVER ": true",
164 "*" XSUBLIM_ARG_OUTLINE": true",
165 "*" XSUBLIM_ARG_CENTER": true",
168 XrmOptionDescRec options[] =
170 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
172 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
174 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
176 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
178 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
180 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
181 XrmoptionNoArg,"true"},
182 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
183 XrmoptionNoArg,"false"},
184 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
186 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
187 XrmoptionNoArg,"true"},
188 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
189 XrmoptionNoArg,"false"},
190 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
191 XrmoptionNoArg,"true"},
192 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
193 XrmoptionNoArg,"false"},
194 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
195 XrmoptionNoArg,"true"},
196 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
197 XrmoptionNoArg,"false"},
201 static int Xsublim_Sig_Last;
204 /* Functions *****************************************************************/
206 /* Defer signals to protect the server grab ================================ */
207 void xsublim_Sig_Catch(int sig_Number)
209 /* BSD needs this reset each time, and it shouldn't hurt anything
211 signal(sig_Number,xsublim_Sig_Catch);
212 Xsublim_Sig_Last = sig_Number;
215 /* Get the screensaver's window ============================================ */
216 static XErrorHandler Xsublim_Ss_Handler = NULL;
217 static int Xsublim_Ss_Status;
219 /* This was all basically swiped from driver/remote.c and util/vroot.h */
220 static int xsublim_Ss_Handler(Display* handle_Display,
221 XErrorEvent* handle_Error)
223 if (handle_Error->error_code == BadWindow)
225 Xsublim_Ss_Status = BadWindow;
228 if (Xsublim_Ss_Handler == NULL)
230 fprintf(stderr,"%x: ",progname);
233 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
235 static Window xsublim_Ss_GetWindow(Display* ss_Display)
238 Window win_RootReturn;
246 unsigned long prop_Count;
247 unsigned long prop_Bytes;
250 static Atom XA_SCREENSAVER_VERSION = -1;
251 static Atom __SWM_VROOT;
253 /* Assume bad things */
258 if (XA_SCREENSAVER_VERSION == -1)
260 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
261 "_SCREENSAVER_VERSION",FALSE);
262 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
265 /* Find a screensaver window */
266 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
267 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
268 &win_Child,&child_Count) != FALSE)
271 (win_Root == win_RootReturn) &&
273 (win_Child != NULL) &&
276 for (child_Index = 0;child_Index < child_Count;
279 XSync(ss_Display,FALSE);
280 Xsublim_Ss_Status = 0;
282 XSetErrorHandler(xsublim_Ss_Handler);
284 prop_Status = XGetWindowProperty(ss_Display,
285 win_Child[child_Index],XA_SCREENSAVER_VERSION,
286 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
287 &prop_Count,&prop_Bytes,
288 (unsigned char**)&prop_Value);
289 XSync(ss_Display,FALSE);
290 XSetErrorHandler(Xsublim_Ss_Handler);
291 if (prop_Value != NULL)
295 if (Xsublim_Ss_Status == BadWindow)
297 prop_Status = BadWindow;
299 if ((prop_Status == Success) &&
302 /* See if it's a virtual root */
305 XGetWindowProperty(ss_Display,
306 win_Child[child_Index],__SWM_VROOT,0,
307 1,FALSE,XA_WINDOW,&prop_Type,
308 &prop_Format,&prop_Count,&prop_Bytes,
309 (unsigned char**)&prop_Value);
310 if (prop_Value != NULL)
314 if ((prop_Status == Success) &&
318 win_Child[child_Index];
324 if (win_Child != NULL)
331 /* Main ==================================================================== */
332 static XErrorHandler Xsublim_Sh_Handler = NULL;
333 static int Xsublim_Sh_Status = 0;
335 static int xsublim_Sh_Handler(Display* handle_Display,
336 XErrorEvent* handle_Error)
338 if (handle_Error->error_code == BadMatch)
340 Xsublim_Sh_Status = BadMatch;
343 if (Xsublim_Sh_Handler == NULL)
345 fprintf(stderr,"%s: ",progname);
348 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
350 int main(int argc,char* argv[])
378 #if defined(SIGDANGER)
384 Display* disp_Display;
386 XWindowAttributes attr_Win;
387 XGCValues gc_ValFore;
388 XGCValues gc_ValBack;
391 XFontStruct* font_Font;
394 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
395 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
396 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
397 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
398 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
399 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
401 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
402 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
403 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
404 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
405 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
415 char* text_List[XSUBLIM_TEXT_COUNT];
416 int text_Used[XSUBLIM_TEXT_COUNT];
417 char text_Text[XSUBLIM_TEXT_LENGTH+1];
435 int text_OutlineIndex;
436 XImage* image_Image = NULL;
440 int image_Height = 0;
444 int arg_FlagScreensaver;
448 int arg_DelayPhraseMin;
449 int arg_DelayPhraseMax;
452 /* Set-up ---------------------------------------------------------- */
455 Xsublim_Sig_Last = -1;
456 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
458 signal(sig_Number,xsublim_Sig_Catch);
461 /* Randomize -- only need to do this here because this program
462 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
464 ya_rand_init ((int) time ((time_t *) 0));
466 /* Handle all the X nonsense */
468 SgiUseSchemes("none");
470 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
474 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
483 int count = (sizeof(options)/sizeof(*options))-1;
484 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
485 fprintf (stderr, "Options include: ");
486 for (i = 0; i < count; i++)
488 char *sw = options [i].option;
489 Bool argp = (options [i].argKind == XrmoptionSepArg);
490 int size = strlen (sw) + (argp ? 6 : 0) + 2;
493 fprintf (stderr, "\n\t\t ");
497 fprintf (stderr, "%s", sw);
498 if (argp) fprintf (stderr, " <arg>");
499 if (i != count-1) fprintf (stderr, ", ");
501 fprintf (stderr, ".\n");
505 disp_Display = XtDisplay(app_App);
506 db = XtDatabase(disp_Display);
507 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
508 win_Root = RootWindowOfScreen(XtScreen(app_App));
509 XtDestroyWidget(app_App);
511 /* Get the arguments */
512 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
513 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
514 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
516 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
517 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
518 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
519 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
521 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
523 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
525 arg_DelayPhraseMax = arg_DelayPhraseMin;
528 /* Get the phrases */
532 memset(text_Used,0,sizeof(text_Used));
533 arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
534 if (arg_Text != NULL)
536 arg_Text = strdup(arg_Text);
537 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
538 (text_Count < XSUBLIM_TEXT_COUNT))
541 text_List[text_Count] = text_Phrase;
545 text_List[text_Count] = NULL;
548 fprintf(stderr,"%s: No text to display\n",progname);
553 font_Font = XLoadQueryFont(disp_Display,
554 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
556 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
558 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
561 if (font_Font == NULL)
563 fprintf(stderr,"%s: Couldn't load a font\n",progname);
568 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
569 gc_ValFore.font = font_Font->fid;
570 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
571 disp_Display,attr_Win.colormap);
572 gc_ValFore.background = get_pixel_resource("background","Background",
573 disp_Display,attr_Win.colormap);
574 gc_ValFore.subwindow_mode = IncludeInferiors;
575 gc_GcFore = XCreateGC(disp_Display,win_Root,
576 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
577 gc_ValBack.font = font_Font->fid;
578 gc_ValBack.foreground = get_pixel_resource("background","Background",
579 disp_Display,attr_Win.colormap);
580 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
581 disp_Display,attr_Win.colormap);
582 gc_ValBack.subwindow_mode = IncludeInferiors;
583 gc_GcBack = XCreateGC(disp_Display,win_Root,
584 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
586 /* Loop ------------------------------------------------------------ */
587 while (Xsublim_Sig_Last == -1)
589 /* Once-per-phrase stuff ----------------------------------- */
591 /* If we're waiting for a screensaver... */
592 if (arg_FlagScreensaver != FALSE)
594 /* Find the screensaver's window */
595 win_Root = xsublim_Ss_GetWindow(disp_Display);
603 /* Pick the next phrase */
604 if (arg_FlagRandom != FALSE)
606 text_Item = random()%text_Count;
609 while (text_Used[text_Item] != FALSE)
613 if (text_Index == text_Count)
616 memset(text_Used,0,sizeof(text_Used));
618 if (text_List[text_Item] == NULL)
623 text_Used[text_Item] = TRUE;
624 strncpy(text_Text,text_List[text_Item],
625 XSUBLIM_TEXT_LENGTH);
626 text_Phrase = text_Text;
628 /* Run through the phrase */
629 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
630 (Xsublim_Sig_Last == -1))
634 /* Once-per-word stuff ----------------------------- */
636 /* Find the text's position */
637 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
638 text_Length = strlen(text_Word);
639 text_Width = XTextWidth(font_Font,text_Word,
640 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
641 text_Height = font_Font->ascent+font_Font->descent+1+
642 XSUBLIM_TEXT_OUTLINE*2;
643 if (arg_FlagCenter == FALSE)
645 text_X = random()%(attr_Win.width-text_Width);
646 text_Y = random()%(attr_Win.height-
651 text_X = (attr_Win.width/2)-(text_Width/2);
652 text_Y = (attr_Win.height/2)-(text_Height/2);
655 /* Find the image's position (and pad it out slightly,
656 otherwise bits of letter get left behind -- are
657 there boundry issues I don't know about?) */
660 image_Width = text_Width+32;
661 image_Height = text_Height;
670 if (image_X+image_Width > attr_Win.width)
672 image_Width = attr_Win.width-image_X;
674 if (image_Y+image_Height > attr_Win.height)
676 image_Height = attr_Win.height-image_Y;
679 /* Influence people for our own ends --------------- */
681 /* Grab the server -- we can't let anybody draw over
683 XSync(disp_Display,FALSE);
684 XGrabServer(disp_Display);
685 XSync(disp_Display,FALSE);
687 /* Set up an error handler that ignores BadMatches --
688 since the screensaver can take its window away at
689 any time, any call that uses it might choke */
690 Xsublim_Sh_Status = 0;
692 XSetErrorHandler(xsublim_Sh_Handler);
694 /* Save the current background */
695 image_Image = XGetImage(disp_Display,win_Root,image_X,
696 image_Y,image_Width,image_Height,~0L,ZPixmap);
698 /* If we've successfully saved the background... */
699 if (image_Image != NULL)
701 if (Xsublim_Sh_Status == 0)
703 /* Draw the outline */
704 if (arg_FlagOutline != FALSE)
706 for (text_OutlineIndex = 0;
708 text_OutlineIndex].outline_X
709 != 0;text_OutlineIndex++)
723 XSUBLIM_TEXT_OUTLINE,
729 XSUBLIM_TEXT_OUTLINE,
736 XDrawString(disp_Display,win_Root,
738 text_Y+(font_Font->ascent),text_Word,
741 if (Xsublim_Sh_Status == 0)
744 XSync(disp_Display,FALSE);
745 if (Xsublim_Sig_Last == -1)
747 usleep(arg_DelayShow);
750 /* Restore the background */
751 XPutImage(disp_Display,win_Root,
752 gc_GcFore,image_Image,0,0,image_X,
753 image_Y,image_Width,image_Height);
757 XDestroyImage(image_Image);
760 /* Restore the error handler, ungrab the server */
761 XSync(disp_Display, FALSE);
762 XSetErrorHandler(Xsublim_Sh_Handler);
763 XUngrabServer(disp_Display);
764 XSync(disp_Display, FALSE);
766 /* Pause between words */
767 if (Xsublim_Sig_Last == -1)
769 usleep(arg_DelayWord);
773 /* Pause between phrases */
774 if (Xsublim_Sig_Last == -1)
776 usleep(random()%(arg_DelayPhraseMax-
777 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
781 /* Exit ------------------------------------------------------------ */
782 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
784 signal(sig_Number,SIG_DFL);
786 kill(getpid(),Xsublim_Sig_Last);