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);
462 srandom((int)time((time_t*)0));
464 /* Handle all the X nonsense */
466 SgiUseSchemes("none");
468 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
472 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
481 int count = (sizeof(options)/sizeof(*options))-1;
482 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
483 fprintf (stderr, "Options include: ");
484 for (i = 0; i < count; i++)
486 char *sw = options [i].option;
487 Bool argp = (options [i].argKind == XrmoptionSepArg);
488 int size = strlen (sw) + (argp ? 6 : 0) + 2;
491 fprintf (stderr, "\n\t\t ");
495 fprintf (stderr, "%s", sw);
496 if (argp) fprintf (stderr, " <arg>");
497 if (i != count-1) fprintf (stderr, ", ");
499 fprintf (stderr, ".\n");
503 disp_Display = XtDisplay(app_App);
504 db = XtDatabase(disp_Display);
505 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
506 win_Root = RootWindowOfScreen(XtScreen(app_App));
507 XtDestroyWidget(app_App);
509 /* Get the arguments */
510 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
511 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
512 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
514 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
515 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
516 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
517 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
519 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
521 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
523 arg_DelayPhraseMax = arg_DelayPhraseMin;
526 /* Get the phrases */
530 memset(text_Used,0,sizeof(text_Used));
531 arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
532 if (arg_Text != NULL)
534 arg_Text = strdup(arg_Text);
535 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
536 (text_Count < XSUBLIM_TEXT_COUNT))
539 text_List[text_Count] = text_Phrase;
543 text_List[text_Count] = NULL;
546 fprintf(stderr,"%s: No text to display\n",progname);
551 font_Font = XLoadQueryFont(disp_Display,
552 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
554 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
556 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
559 if (font_Font == NULL)
561 fprintf(stderr,"%s: Couldn't load a font\n",progname);
566 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
567 gc_ValFore.font = font_Font->fid;
568 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
569 disp_Display,attr_Win.colormap);
570 gc_ValFore.background = get_pixel_resource("background","Background",
571 disp_Display,attr_Win.colormap);
572 gc_ValFore.subwindow_mode = IncludeInferiors;
573 gc_GcFore = XCreateGC(disp_Display,win_Root,
574 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
575 gc_ValBack.font = font_Font->fid;
576 gc_ValBack.foreground = get_pixel_resource("background","Background",
577 disp_Display,attr_Win.colormap);
578 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
579 disp_Display,attr_Win.colormap);
580 gc_ValBack.subwindow_mode = IncludeInferiors;
581 gc_GcBack = XCreateGC(disp_Display,win_Root,
582 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
584 /* Loop ------------------------------------------------------------ */
585 while (Xsublim_Sig_Last == -1)
587 /* Once-per-phrase stuff ----------------------------------- */
589 /* If we're waiting for a screensaver... */
590 if (arg_FlagScreensaver != FALSE)
592 /* Find the screensaver's window */
593 win_Root = xsublim_Ss_GetWindow(disp_Display);
601 /* Pick the next phrase */
602 if (arg_FlagRandom != FALSE)
604 text_Item = random()%text_Count;
607 while (text_Used[text_Item] != FALSE)
611 if (text_Index == text_Count)
614 memset(text_Used,0,sizeof(text_Used));
616 if (text_List[text_Item] == NULL)
621 text_Used[text_Item] = TRUE;
622 strncpy(text_Text,text_List[text_Item],
623 XSUBLIM_TEXT_LENGTH);
624 text_Phrase = text_Text;
626 /* Run through the phrase */
627 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
628 (Xsublim_Sig_Last == -1))
632 /* Once-per-word stuff ----------------------------- */
634 /* Find the text's position */
635 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
636 text_Length = strlen(text_Word);
637 text_Width = XTextWidth(font_Font,text_Word,
638 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
639 text_Height = font_Font->ascent+font_Font->descent+1+
640 XSUBLIM_TEXT_OUTLINE*2;
641 if (arg_FlagCenter == FALSE)
643 text_X = random()%(attr_Win.width-text_Width);
644 text_Y = random()%(attr_Win.height-
649 text_X = (attr_Win.width/2)-(text_Width/2);
650 text_Y = (attr_Win.height/2)-(text_Height/2);
653 /* Find the image's position (and pad it out slightly,
654 otherwise bits of letter get left behind -- are
655 there boundry issues I don't know about?) */
658 image_Width = text_Width+32;
659 image_Height = text_Height;
668 if (image_X+image_Width > attr_Win.width)
670 image_Width = attr_Win.width-image_X;
672 if (image_Y+image_Height > attr_Win.height)
674 image_Height = attr_Win.height-image_Y;
677 /* Influence people for our own ends --------------- */
679 /* Grab the server -- we can't let anybody draw over
681 XSync(disp_Display,FALSE);
682 XGrabServer(disp_Display);
683 XSync(disp_Display,FALSE);
685 /* Set up an error handler that ignores BadMatches --
686 since the screensaver can take its window away at
687 any time, any call that uses it might choke */
688 Xsublim_Sh_Status = 0;
690 XSetErrorHandler(xsublim_Sh_Handler);
692 /* Save the current background */
693 image_Image = XGetImage(disp_Display,win_Root,image_X,
694 image_Y,image_Width,image_Height,~0L,ZPixmap);
696 /* If we've successfully saved the background... */
697 if (image_Image != NULL)
699 if (Xsublim_Sh_Status == 0)
701 /* Draw the outline */
702 if (arg_FlagOutline != FALSE)
704 for (text_OutlineIndex = 0;
706 text_OutlineIndex].outline_X
707 != 0;text_OutlineIndex++)
721 XSUBLIM_TEXT_OUTLINE,
727 XSUBLIM_TEXT_OUTLINE,
734 XDrawString(disp_Display,win_Root,
736 text_Y+(font_Font->ascent),text_Word,
739 if (Xsublim_Sh_Status == 0)
742 XSync(disp_Display,FALSE);
743 if (Xsublim_Sig_Last == -1)
745 usleep(arg_DelayShow);
748 /* Restore the background */
749 XPutImage(disp_Display,win_Root,
750 gc_GcFore,image_Image,0,0,image_X,
751 image_Y,image_Width,image_Height);
755 XDestroyImage(image_Image);
758 /* Restore the error handler, ungrab the server */
759 XSync(disp_Display, FALSE);
760 XSetErrorHandler(Xsublim_Sh_Handler);
761 XUngrabServer(disp_Display);
762 XSync(disp_Display, FALSE);
764 /* Pause between words */
765 if (Xsublim_Sig_Last == -1)
767 usleep(arg_DelayWord);
771 /* Pause between phrases */
772 if (Xsublim_Sig_Last == -1)
774 usleep(random()%(arg_DelayPhraseMax-
775 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
779 /* Exit ------------------------------------------------------------ */
780 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
782 signal(sig_Number,SIG_DFL);
784 kill(getpid(),Xsublim_Sig_Last);