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 -delayShow ms Microsecs for display of each word
35 -delayWord ms Microsecs for blank between words
36 -delayPhraseMin ms Microsecs for min blank between phrases
37 -delayPhraseMax ms Microsecs for max blank between phrases
38 -random Show phrases in random order (Default)
39 -no-random Show phrases in listed order
40 -screensaver Wait for an active screensaver (Default)
41 -no-screensaver Draw over active windows
42 -outline Draw a contrasting outline around words (Default)
43 -no-outline Draw words without an outline
44 -center Draw words in the center of the screen (Default)
45 -no-center Draw words randomly around the screen
49 /* Defines *******************************************************************/
50 #define XSUBLIM_NAME "XSublim"
51 #define XSUBLIM_TEXT_COUNT 1000
52 #define XSUBLIM_TEXT_LENGTH 128
53 #define XSUBLIM_TEXT_OUTLINE 1
54 #define XSUBLIM_ARG_DELAYSHOW "delayShow"
55 #define XSUBLIM_ARG_DELAYWORD "delayWord"
56 #define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
57 #define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
58 #define XSUBLIM_ARG_RANDOM "random"
59 #define XSUBLIM_ARG_FILE "file"
60 #define XSUBLIM_ARG_SCREENSAVER "screensaver"
61 #define XSUBLIM_ARG_OUTLINE "outline"
62 #define XSUBLIM_ARG_CENTER "center"
63 #define XSUBLIM_ARG_FONT "font"
64 #define XSUBLIM_ARG_PHRASES "phrases"
71 /* Includes ******************************************************************/
76 #include <X11/Intrinsic.h>
77 #include <X11/IntrinsicP.h>
78 #include <X11/CoreP.h>
79 #include <X11/Shell.h>
80 #include <X11/StringDefs.h>
81 #include <X11/Xutil.h>
82 #include <X11/keysym.h>
83 #include <X11/Xatom.h>
86 #include <X11/Xproto.h>
88 #include <X11/SGIScheme.h>
92 #include "resources.h"
95 /* Globals *******************************************************************/
99 char* progclass = XSUBLIM_NAME;
102 ".background: #000000",
103 ".foreground: #FFFFFF",
104 "*" XSUBLIM_ARG_PHRASES ":"
108 "OBEY. OBEY. OBEY.\\n"
117 "Despair quietly.\\n"
119 "You are being watched.\\n"
120 "You will be punished.\\n"
121 "You serve no purpose.\\n"
122 "Your contributions are ignored.\\n"
123 "They are laughing at you.\\n"
124 "They lied to you.\\n"
125 "They read your mail.\\n"
130 "You are a prisoner.\\n"
131 "You are helpless.\\n"
132 "You are diseased.\\n"
133 "Fear the unknown.\\n"
134 "Happiness follows obedience.\\n"
135 "Ignorance is strength.\\n"
137 "Freedom is slavery.\\n"
138 "Abandon all hope.\\n"
139 "You will be assimilated.\\n"
140 "Resistance is futile.\\n"
141 "Resistance is useless.\\n"
144 "What's that smell?\\n"
145 "All praise the company.\\n"
147 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
148 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
149 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
150 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
151 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
152 "*" XSUBLIM_ARG_RANDOM ": true",
153 "*" XSUBLIM_ARG_SCREENSAVER ": true",
154 "*" XSUBLIM_ARG_OUTLINE": true",
155 "*" XSUBLIM_ARG_CENTER": true",
158 XrmOptionDescRec options[] =
160 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
162 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
164 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
166 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
168 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
170 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
171 XrmoptionNoArg,"true"},
172 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
173 XrmoptionNoArg,"false"},
174 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
176 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
177 XrmoptionNoArg,"true"},
178 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
179 XrmoptionNoArg,"false"},
180 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
181 XrmoptionNoArg,"true"},
182 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
183 XrmoptionNoArg,"false"},
184 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
185 XrmoptionNoArg,"true"},
186 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
187 XrmoptionNoArg,"false"},
191 static int Xsublim_Sig_Last;
194 /* Functions *****************************************************************/
196 /* Defer signals to protect the server grab ================================ */
197 void xsublim_Sig_Catch(int sig_Number)
199 /* BSD needs this reset each time, and it shouldn't hurt anything
201 signal(sig_Number,xsublim_Sig_Catch);
202 Xsublim_Sig_Last = sig_Number;
205 /* Get the screensaver's window ============================================ */
206 static XErrorHandler Xsublim_Ss_Handler = NULL;
207 static int Xsublim_Ss_Status;
209 /* This was all basically swiped from driver/remote.c and util/vroot.h */
210 static int xsublim_Ss_Handler(Display* handle_Display,
211 XErrorEvent* handle_Error)
213 if (handle_Error->error_code == BadWindow)
215 Xsublim_Ss_Status = BadWindow;
218 if (Xsublim_Ss_Handler == NULL)
220 fprintf(stderr,"%x: ",progname);
223 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
225 static Window xsublim_Ss_GetWindow(Display* ss_Display)
228 Window win_RootReturn;
236 unsigned long prop_Count;
237 unsigned long prop_Bytes;
240 static Atom XA_SCREENSAVER_VERSION = -1;
241 static Atom __SWM_VROOT;
243 /* Assume bad things */
248 if (XA_SCREENSAVER_VERSION == -1)
250 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
251 "_SCREENSAVER_VERSION",FALSE);
252 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
255 /* Find a screensaver window */
256 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
257 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
258 &win_Child,&child_Count) != FALSE)
261 (win_Root == win_RootReturn) &&
263 (win_Child != NULL) &&
266 for (child_Index = 0;child_Index < child_Count;
269 XSync(ss_Display,FALSE);
270 Xsublim_Ss_Status = 0;
272 XSetErrorHandler(xsublim_Ss_Handler);
274 prop_Status = XGetWindowProperty(ss_Display,
275 win_Child[child_Index],XA_SCREENSAVER_VERSION,
276 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
277 &prop_Count,&prop_Bytes,
278 (unsigned char**)&prop_Value);
279 XSync(ss_Display,FALSE);
280 XSetErrorHandler(Xsublim_Ss_Handler);
281 if (prop_Value != NULL)
285 if (Xsublim_Ss_Status == BadWindow)
287 prop_Status = BadWindow;
289 if ((prop_Status == Success) &&
292 /* See if it's a virtual root */
295 XGetWindowProperty(ss_Display,
296 win_Child[child_Index],__SWM_VROOT,0,
297 1,FALSE,XA_WINDOW,&prop_Type,
298 &prop_Format,&prop_Count,&prop_Bytes,
299 (unsigned char**)&prop_Value);
300 if (prop_Value != NULL)
304 if ((prop_Status == Success) &&
308 win_Child[child_Index];
314 if (win_Child != NULL)
321 /* Main ==================================================================== */
322 static XErrorHandler Xsublim_Sh_Handler = NULL;
323 static int Xsublim_Sh_Status = 0;
325 static int xsublim_Sh_Handler(Display* handle_Display,
326 XErrorEvent* handle_Error)
328 if (handle_Error->error_code == BadMatch)
330 Xsublim_Sh_Status = BadMatch;
333 if (Xsublim_Sh_Handler == NULL)
335 fprintf(stderr,"%s: ",progname);
338 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
340 int main(int argc,char* argv[])
368 #if defined(SIGDANGER)
374 Display* disp_Display;
376 XWindowAttributes attr_Win;
377 XGCValues gc_ValFore;
378 XGCValues gc_ValBack;
381 XFontStruct* font_Font;
384 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
385 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
386 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
387 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
388 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
389 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
391 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
392 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
393 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
394 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
395 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
405 char* text_List[XSUBLIM_TEXT_COUNT];
406 int text_Used[XSUBLIM_TEXT_COUNT];
407 char text_Text[XSUBLIM_TEXT_LENGTH+1];
425 int text_OutlineIndex;
426 XImage* image_Image = NULL;
430 int image_Height = 0;
434 int arg_FlagScreensaver;
438 int arg_DelayPhraseMin;
439 int arg_DelayPhraseMax;
442 /* Set-up ---------------------------------------------------------- */
445 Xsublim_Sig_Last = -1;
446 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
448 signal(sig_Number,xsublim_Sig_Catch);
452 srandom((int)time((time_t*)0));
454 /* Handle all the X nonsense */
456 SgiUseSchemes("none");
458 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
462 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
471 int count = (sizeof(options)/sizeof(*options))-1;
472 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
473 fprintf (stderr, "Options include: ");
474 for (i = 0; i < count; i++)
476 char *sw = options [i].option;
477 Bool argp = (options [i].argKind == XrmoptionSepArg);
478 int size = strlen (sw) + (argp ? 6 : 0) + 2;
481 fprintf (stderr, "\n\t\t ");
485 fprintf (stderr, "%s", sw);
486 if (argp) fprintf (stderr, " <arg>");
487 if (i != count-1) fprintf (stderr, ", ");
489 fprintf (stderr, ".\n");
493 disp_Display = XtDisplay(app_App);
494 db = XtDatabase(disp_Display);
495 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
496 win_Root = RootWindowOfScreen(XtScreen(app_App));
497 XtDestroyWidget(app_App);
499 /* Get the arguments */
500 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
501 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
502 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
504 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
505 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
506 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
507 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
509 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
511 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
513 arg_DelayPhraseMax = arg_DelayPhraseMin;
516 /* Get the phrases */
520 memset(text_Used,0,sizeof(text_Used));
521 arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
522 if (arg_Text != NULL)
524 arg_Text = strdup(arg_Text);
525 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
526 (text_Count < XSUBLIM_TEXT_COUNT))
529 text_List[text_Count] = text_Phrase;
533 text_List[text_Count] = NULL;
536 fprintf(stderr,"%s: No text to display\n",progname);
541 font_Font = XLoadQueryFont(disp_Display,
542 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
544 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
546 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
549 if (font_Font == NULL)
551 fprintf(stderr,"%s: Couldn't load a font\n",progname);
556 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
557 gc_ValFore.font = font_Font->fid;
558 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
559 disp_Display,attr_Win.colormap);
560 gc_ValFore.background = get_pixel_resource("background","Background",
561 disp_Display,attr_Win.colormap);
562 gc_ValFore.subwindow_mode = IncludeInferiors;
563 gc_GcFore = XCreateGC(disp_Display,win_Root,
564 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
565 gc_ValBack.font = font_Font->fid;
566 gc_ValBack.foreground = get_pixel_resource("background","Background",
567 disp_Display,attr_Win.colormap);
568 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
569 disp_Display,attr_Win.colormap);
570 gc_ValBack.subwindow_mode = IncludeInferiors;
571 gc_GcBack = XCreateGC(disp_Display,win_Root,
572 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
574 /* Loop ------------------------------------------------------------ */
575 while (Xsublim_Sig_Last == -1)
577 /* Once-per-phrase stuff ----------------------------------- */
579 /* If we're waiting for a screensaver... */
580 if (arg_FlagScreensaver != FALSE)
582 /* Find the screensaver's window */
583 win_Root = xsublim_Ss_GetWindow(disp_Display);
591 /* Pick the next phrase */
592 if (arg_FlagRandom != FALSE)
594 text_Item = random()%text_Count;
597 while (text_Used[text_Item] != FALSE)
601 if (text_Index == text_Count)
604 memset(text_Used,0,sizeof(text_Used));
606 if (text_List[text_Item] == NULL)
611 text_Used[text_Item] = TRUE;
612 strncpy(text_Text,text_List[text_Item],
613 XSUBLIM_TEXT_LENGTH);
614 text_Phrase = text_Text;
616 /* Run through the phrase */
617 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
618 (Xsublim_Sig_Last == -1))
622 /* Once-per-word stuff ----------------------------- */
624 /* Find the text's position */
625 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
626 text_Length = strlen(text_Word);
627 text_Width = XTextWidth(font_Font,text_Word,
628 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
629 text_Height = font_Font->ascent+font_Font->descent+1+
630 XSUBLIM_TEXT_OUTLINE*2;
631 if (arg_FlagCenter == FALSE)
633 text_X = random()%(attr_Win.width-text_Width);
634 text_Y = random()%(attr_Win.height-
639 text_X = (attr_Win.width/2)-(text_Width/2);
640 text_Y = (attr_Win.height/2)-(text_Height/2);
643 /* Find the image's position (and pad it out slightly,
644 otherwise bits of letter get left behind -- are
645 there boundry issues I don't know about?) */
648 image_Width = text_Width+32;
649 image_Height = text_Height;
658 if (image_X+image_Width > attr_Win.width)
660 image_Width = attr_Win.width-image_X;
662 if (image_Y+image_Height > attr_Win.height)
664 image_Height = attr_Win.height-image_Y;
667 /* Influence people for our own ends --------------- */
669 /* Grab the server -- we can't let anybody draw over
671 XSync(disp_Display,FALSE);
672 XGrabServer(disp_Display);
673 XSync(disp_Display,FALSE);
675 /* Set up an error handler that ignores BadMatches --
676 since the screensaver can take its window away at
677 any time, any call that uses it might choke */
678 Xsublim_Sh_Status = 0;
680 XSetErrorHandler(xsublim_Sh_Handler);
682 /* Save the current background */
683 image_Image = XGetImage(disp_Display,win_Root,image_X,
684 image_Y,image_Width,image_Height,~0L,ZPixmap);
686 /* If we've successfully saved the background... */
687 if (image_Image != NULL)
689 if (Xsublim_Sh_Status == 0)
691 /* Draw the outline */
692 if (arg_FlagOutline != FALSE)
694 for (text_OutlineIndex = 0;
696 text_OutlineIndex].outline_X
697 != 0;text_OutlineIndex++)
711 XSUBLIM_TEXT_OUTLINE,
717 XSUBLIM_TEXT_OUTLINE,
724 XDrawString(disp_Display,win_Root,
726 text_Y+(font_Font->ascent),text_Word,
729 if (Xsublim_Sh_Status == 0)
732 XSync(disp_Display,FALSE);
733 if (Xsublim_Sig_Last == -1)
735 usleep(arg_DelayShow);
738 /* Restore the background */
739 XPutImage(disp_Display,win_Root,
740 gc_GcFore,image_Image,0,0,image_X,
741 image_Y,image_Width,image_Height);
744 /* Free the image (and it's goddamned structure
745 -- the man page for XCreateImage() lies,
747 XDestroyImage(image_Image);
751 /* Restore the error handler, ungrab the server */
752 XSync(disp_Display, FALSE);
753 XSetErrorHandler(Xsublim_Sh_Handler);
754 XUngrabServer(disp_Display);
755 XSync(disp_Display, FALSE);
757 /* Pause between words */
758 if (Xsublim_Sig_Last == -1)
760 usleep(arg_DelayWord);
764 /* Pause between phrases */
765 if (Xsublim_Sig_Last == -1)
767 usleep(random()%(arg_DelayPhraseMax-
768 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
772 /* Exit ------------------------------------------------------------ */
773 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
775 signal(sig_Number,SIG_DFL);
777 kill(getpid(),Xsublim_Sig_Last);