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
47 /* Defines *******************************************************************/
48 #define XSUBLIM_NAME "XSublim"
49 #define XSUBLIM_TEXT_COUNT 1000
50 #define XSUBLIM_TEXT_LENGTH 128
51 #define XSUBLIM_TEXT_OUTLINE 1
52 #define XSUBLIM_ARG_DELAYSHOW "delayShow"
53 #define XSUBLIM_ARG_DELAYWORD "delayWord"
54 #define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
55 #define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
56 #define XSUBLIM_ARG_RANDOM "random"
57 #define XSUBLIM_ARG_FILE "file"
58 #define XSUBLIM_ARG_SCREENSAVER "screensaver"
59 #define XSUBLIM_ARG_OUTLINE "outline"
60 #define XSUBLIM_ARG_CENTER "center"
61 #define XSUBLIM_ARG_FONT "font"
62 #define XSUBLIM_ARG_PHRASES "phrases"
69 /* Includes ******************************************************************/
74 #include <X11/Intrinsic.h>
75 #include <X11/IntrinsicP.h>
76 #include <X11/CoreP.h>
77 #include <X11/Shell.h>
78 #include <X11/StringDefs.h>
79 #include <X11/Xutil.h>
80 #include <X11/keysym.h>
81 #include <X11/Xatom.h>
84 #include <X11/Xproto.h>
86 #include <X11/SGIScheme.h>
90 #include "resources.h"
93 /* Globals *******************************************************************/
97 char* progclass = XSUBLIM_NAME;
100 ".background: #000000",
101 ".foreground: #FFFFFF",
102 "*" XSUBLIM_ARG_PHRASES ":"
113 "Despair quietly.\\n"
115 "You are being watched.\\n"
116 "You will be punished.\\n"
117 "You serve no purpose.\\n"
118 "Your contributions are ignored.\\n"
119 "They are laughing at you.\\n"
123 "You are a prisoner.\\n"
124 "You are helpless.\\n"
125 "You are diseased.\\n"
126 "Fear the unknown.\\n"
127 "Happiness follows obedience.\\n"
128 "Ignorance is strength.\\n"
130 "Freedom is slavery.\\n"
131 "Abandon all hope.\\n"
132 "You will be assimilated.\\n"
133 "Resistance is futile.\\n"
134 "Resistance is useless.\\n"
137 "What's that smell?\\n"
138 "All praise the company.\\n"
140 "*" XSUBLIM_ARG_FONT ": -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
141 "*" XSUBLIM_ARG_DELAYSHOW ": 40000",
142 "*" XSUBLIM_ARG_DELAYWORD ": 100000",
143 "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
144 "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
145 "*" XSUBLIM_ARG_RANDOM ": true",
146 "*" XSUBLIM_ARG_SCREENSAVER ": true",
147 "*" XSUBLIM_ARG_OUTLINE": true",
148 "*" XSUBLIM_ARG_CENTER": true",
151 XrmOptionDescRec options[] =
153 {"-" XSUBLIM_ARG_FONT, "." XSUBLIM_ARG_FONT,
155 {"-" XSUBLIM_ARG_DELAYSHOW, "." XSUBLIM_ARG_DELAYSHOW,
157 {"-" XSUBLIM_ARG_DELAYWORD, "." XSUBLIM_ARG_DELAYWORD,
159 {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
161 {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
163 {"-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
164 XrmoptionNoArg,"true"},
165 {"-no-" XSUBLIM_ARG_RANDOM, "." XSUBLIM_ARG_RANDOM,
166 XrmoptionNoArg,"false"},
167 {"-" XSUBLIM_ARG_FILE, "." XSUBLIM_ARG_FILE,
169 {"-" XSUBLIM_ARG_SCREENSAVER, "." XSUBLIM_ARG_SCREENSAVER,
170 XrmoptionNoArg,"true"},
171 {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
172 XrmoptionNoArg,"false"},
173 {"-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
174 XrmoptionNoArg,"true"},
175 {"-no-" XSUBLIM_ARG_OUTLINE, "." XSUBLIM_ARG_OUTLINE,
176 XrmoptionNoArg,"false"},
177 {"-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
178 XrmoptionNoArg,"true"},
179 {"-no-" XSUBLIM_ARG_CENTER, "." XSUBLIM_ARG_CENTER,
180 XrmoptionNoArg,"false"},
184 static int Xsublim_Sig_Last;
187 /* Functions *****************************************************************/
189 /* Defer signals to protect the server grab ================================ */
190 void xsublim_Sig_Catch(int sig_Number)
192 /* BSD needs this reset each time, and it shouldn't hurt anything
194 signal(sig_Number,xsublim_Sig_Catch);
195 Xsublim_Sig_Last = sig_Number;
198 /* Get the screensaver's window ============================================ */
199 static XErrorHandler Xsublim_Ss_Handler = NULL;
200 static int Xsublim_Ss_Status;
202 /* This was all basically swiped from driver/remote.c and util/vroot.h */
203 static int xsublim_Ss_Handler(Display* handle_Display,
204 XErrorEvent* handle_Error)
206 if (handle_Error->error_code == BadWindow)
208 Xsublim_Ss_Status = BadWindow;
211 if (Xsublim_Ss_Handler == NULL)
213 fprintf(stderr,"%x: ",progname);
216 return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
218 static Window xsublim_Ss_GetWindow(Display* ss_Display)
221 Window win_RootReturn;
229 unsigned long prop_Count;
230 unsigned long prop_Bytes;
233 static Atom XA_SCREENSAVER_VERSION = -1;
234 static Atom __SWM_VROOT;
236 /* Assume bad things */
241 if (XA_SCREENSAVER_VERSION == -1)
243 XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
244 "_SCREENSAVER_VERSION",FALSE);
245 __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
248 /* Find a screensaver window */
249 win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
250 if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
251 &win_Child,&child_Count) != FALSE)
254 (win_Root == win_RootReturn) &&
256 (win_Child != NULL) &&
259 for (child_Index = 0;child_Index < child_Count;
262 XSync(ss_Display,FALSE);
263 Xsublim_Ss_Status = 0;
265 XSetErrorHandler(xsublim_Ss_Handler);
267 prop_Status = XGetWindowProperty(ss_Display,
268 win_Child[child_Index],XA_SCREENSAVER_VERSION,
269 0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
270 &prop_Count,&prop_Bytes,
271 (unsigned char**)&prop_Value);
272 XSync(ss_Display,FALSE);
273 XSetErrorHandler(Xsublim_Ss_Handler);
274 if (prop_Value != NULL)
278 if (Xsublim_Ss_Status == BadWindow)
280 prop_Status = BadWindow;
282 if ((prop_Status == Success) &&
285 /* See if it's a virtual root */
288 XGetWindowProperty(ss_Display,
289 win_Child[child_Index],__SWM_VROOT,0,
290 1,FALSE,XA_WINDOW,&prop_Type,
291 &prop_Format,&prop_Count,&prop_Bytes,
292 (unsigned char**)&prop_Value);
293 if (prop_Value != NULL)
297 if ((prop_Status == Success) &&
301 win_Child[child_Index];
307 if (win_Child != NULL)
314 /* Main ==================================================================== */
315 static XErrorHandler Xsublim_Sh_Handler = NULL;
316 static int Xsublim_Sh_Status = 0;
318 static int xsublim_Sh_Handler(Display* handle_Display,
319 XErrorEvent* handle_Error)
321 if (handle_Error->error_code == BadMatch)
323 Xsublim_Sh_Status = BadMatch;
326 if (Xsublim_Sh_Handler == NULL)
328 fprintf(stderr,"%s: ",progname);
331 return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
333 int main(int argc,char* argv[])
361 #if defined(SIGDANGER)
367 Display* disp_Display;
369 XWindowAttributes attr_Win;
370 XGCValues gc_ValFore;
371 XGCValues gc_ValBack;
374 XFontStruct* font_Font;
377 "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
378 "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
379 "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
380 "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
381 "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
382 "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
384 "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
385 "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
386 "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
387 "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
388 "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
398 char* text_List[XSUBLIM_TEXT_COUNT];
399 int text_Used[XSUBLIM_TEXT_COUNT];
400 char text_Text[XSUBLIM_TEXT_LENGTH+1];
418 int text_OutlineIndex;
419 XImage* image_Image = NULL;
423 int image_Height = 0;
427 int arg_FlagScreensaver;
431 int arg_DelayPhraseMin;
432 int arg_DelayPhraseMax;
435 /* Set-up ---------------------------------------------------------- */
438 Xsublim_Sig_Last = -1;
439 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
441 signal(sig_Number,xsublim_Sig_Catch);
445 srandom((int)time((time_t*)0));
447 /* Handle all the X nonsense */
449 SgiUseSchemes("none");
451 for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
455 app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
464 int count = (sizeof(options)/sizeof(*options))-1;
465 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
466 fprintf (stderr, "Options include: ");
467 for (i = 0; i < count; i++)
469 char *sw = options [i].option;
470 Bool argp = (options [i].argKind == XrmoptionSepArg);
471 int size = strlen (sw) + (argp ? 6 : 0) + 2;
474 fprintf (stderr, "\n\t\t ");
478 fprintf (stderr, "%s", sw);
479 if (argp) fprintf (stderr, " <arg>");
480 if (i != count-1) fprintf (stderr, ", ");
482 fprintf (stderr, ".\n");
486 disp_Display = XtDisplay(app_App);
487 db = XtDatabase(disp_Display);
488 XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
489 win_Root = RootWindowOfScreen(XtScreen(app_App));
490 XtDestroyWidget(app_App);
492 /* Get the arguments */
493 arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
494 arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
495 arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
497 arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
498 arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
499 arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
500 arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
502 arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
504 if (arg_DelayPhraseMax < arg_DelayPhraseMin)
506 arg_DelayPhraseMax = arg_DelayPhraseMin;
509 /* Get the phrases */
513 memset(text_Used,0,sizeof(text_Used));
514 arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
515 if (arg_Text != NULL)
517 arg_Text = strdup(arg_Text);
518 while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
519 (text_Count < XSUBLIM_TEXT_COUNT))
522 text_List[text_Count] = text_Phrase;
526 text_List[text_Count] = NULL;
529 fprintf(stderr,"%s: No text to display\n",progname);
534 font_Font = XLoadQueryFont(disp_Display,
535 get_string_resource(XSUBLIM_ARG_FONT,"Font"));
537 while ((font_Font == NULL) && (font_List[font_Index] != NULL))
539 font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
542 if (font_Font == NULL)
544 fprintf(stderr,"%s: Couldn't load a font\n",progname);
549 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
550 gc_ValFore.font = font_Font->fid;
551 gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
552 disp_Display,attr_Win.colormap);
553 gc_ValFore.background = get_pixel_resource("background","Background",
554 disp_Display,attr_Win.colormap);
555 gc_ValFore.subwindow_mode = IncludeInferiors;
556 gc_GcFore = XCreateGC(disp_Display,win_Root,
557 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
558 gc_ValBack.font = font_Font->fid;
559 gc_ValBack.foreground = get_pixel_resource("background","Background",
560 disp_Display,attr_Win.colormap);
561 gc_ValBack.background = get_pixel_resource("foreground","Foreground",
562 disp_Display,attr_Win.colormap);
563 gc_ValBack.subwindow_mode = IncludeInferiors;
564 gc_GcBack = XCreateGC(disp_Display,win_Root,
565 (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
567 /* Loop ------------------------------------------------------------ */
568 while (Xsublim_Sig_Last == -1)
570 /* Once-per-phrase stuff ----------------------------------- */
572 /* If we're waiting for a screensaver... */
573 if (arg_FlagScreensaver != FALSE)
575 /* Find the screensaver's window */
576 win_Root = xsublim_Ss_GetWindow(disp_Display);
584 /* Pick the next phrase */
585 if (arg_FlagRandom != FALSE)
587 text_Item = random()%text_Count;
590 while (text_Used[text_Item] != FALSE)
594 if (text_Index == text_Count)
597 memset(text_Used,0,sizeof(text_Used));
599 if (text_List[text_Item] == NULL)
604 text_Used[text_Item] = TRUE;
605 strncpy(text_Text,text_List[text_Item],
606 XSUBLIM_TEXT_LENGTH);
607 text_Phrase = text_Text;
609 /* Run through the phrase */
610 while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
611 (Xsublim_Sig_Last == -1))
615 /* Once-per-word stuff ----------------------------- */
617 /* Find the text's position */
618 XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
619 text_Length = strlen(text_Word);
620 text_Width = XTextWidth(font_Font,text_Word,
621 text_Length)+XSUBLIM_TEXT_OUTLINE*2;
622 text_Height = font_Font->ascent+font_Font->descent+1+
623 XSUBLIM_TEXT_OUTLINE*2;
624 if (arg_FlagCenter == FALSE)
626 text_X = random()%(attr_Win.width-text_Width);
627 text_Y = random()%(attr_Win.height-
632 text_X = (attr_Win.width/2)-(text_Width/2);
633 text_Y = (attr_Win.height/2)-(text_Height/2);
636 /* Find the image's position (and pad it out slightly,
637 otherwise bits of letter get left behind -- are
638 there boundry issues I don't know about?) */
641 image_Width = text_Width+32;
642 image_Height = text_Height;
651 if (image_X+image_Width > attr_Win.width)
653 image_Width = attr_Win.width-image_X;
655 if (image_Y+image_Height > attr_Win.height)
657 image_Height = attr_Win.height-image_Y;
660 /* Influence people for our own ends --------------- */
662 /* Grab the server -- we can't let anybody draw over
664 XSync(disp_Display,FALSE);
665 XGrabServer(disp_Display);
666 XSync(disp_Display,FALSE);
668 /* Set up an error handler that ignores BadMatches --
669 since the screensaver can take its window away at
670 any time, any call that uses it might choke */
671 Xsublim_Sh_Status = 0;
673 XSetErrorHandler(xsublim_Sh_Handler);
675 /* Save the current background */
676 image_Image = XGetImage(disp_Display,win_Root,image_X,
677 image_Y,image_Width,image_Height,~0L,ZPixmap);
679 /* If we've successfully saved the background... */
680 if (image_Image != NULL)
682 if (Xsublim_Sh_Status == 0)
684 /* Draw the outline */
685 if (arg_FlagOutline != FALSE)
687 for (text_OutlineIndex = 0;
689 text_OutlineIndex].outline_X
690 != 0;text_OutlineIndex++)
704 XSUBLIM_TEXT_OUTLINE,
710 XSUBLIM_TEXT_OUTLINE,
717 XDrawString(disp_Display,win_Root,
719 text_Y+(font_Font->ascent),text_Word,
722 if (Xsublim_Sh_Status == 0)
725 XSync(disp_Display,FALSE);
726 if (Xsublim_Sig_Last == -1)
728 usleep(arg_DelayShow);
731 /* Restore the background */
732 XPutImage(disp_Display,win_Root,
733 gc_GcFore,image_Image,0,0,image_X,
734 image_Y,image_Width,image_Height);
737 /* Free the image (and it's goddamned structure
738 -- the man page for XCreateImage() lies,
740 XDestroyImage(image_Image);
744 /* Restore the error handler, ungrab the server */
745 XSync(disp_Display, FALSE);
746 XSetErrorHandler(Xsublim_Sh_Handler);
747 XUngrabServer(disp_Display);
748 XSync(disp_Display, FALSE);
750 /* Pause between words */
751 if (Xsublim_Sig_Last == -1)
753 usleep(arg_DelayWord);
757 /* Pause between phrases */
758 if (Xsublim_Sig_Last == -1)
760 usleep(random()%(arg_DelayPhraseMax-
761 arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
765 /* Exit ------------------------------------------------------------ */
766 for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
768 signal(sig_Number,SIG_DFL);
770 kill(getpid(),Xsublim_Sig_Last);