From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / xjack.c
1 /* xscreensaver, Copyright (c) 1997-2013 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Wendy, let me explain something to you.  Whenever you come in here and
12  * interrupt me, you're BREAKING my CONCENTRATION.  You're DISTRACTING me!
13  * And it will then take me time to get back to where I was. You understand?
14  * Now, we're going to make a new rule.  When you come in here and you hear
15  * me typing, or whether you DON'T hear me typing, or whatever the FUCK you
16  * hear me doing; when I'm in here, it means that I am working, THAT means
17  * don't come in!  Now, do you think you can handle that?
18  */
19
20 #include <ctype.h>
21 #include "screenhack.h"
22
23 static const char *source = "All work and no play makes Jack a dull boy.  ";
24 /* If you're here because you're thinking about making the above string be
25    customizable, then you don't get the joke.  You loser. */
26
27 struct state {
28   Display *dpy;
29   Window window;
30   XWindowAttributes xgwa;
31   XFontStruct *font;
32   GC gc;
33
34   const char *s;
35   int columns, rows;            /* characters */
36   int left, right;              /* characters */
37   int char_width, line_height;  /* pixels */
38   int x, y;                     /* characters */
39   int mode;
40   int hspace;                   /* pixels */
41   int vspace;                   /* pixels */
42   Bool break_para;
43   Bool caps;
44   int sentences;
45   int paras;
46   int scrolling;
47   int subscrolling;
48   int pining;
49
50   int delay;
51 };
52
53
54 static void
55 xjack_reshape (Display *dpy, Window window, void *closure, 
56                  unsigned int w, unsigned int h)
57 {
58   struct state *st = (struct state *) closure;
59   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
60   st->columns = (st->xgwa.width  - st->hspace - st->hspace) / st->char_width;
61   st->rows    = (st->xgwa.height - st->vspace - st->vspace) / st->line_height;
62   st->rows--;
63   st->columns--;
64
65   /* If the window is stupidly small, just truncate. */
66   if (st->rows < 4)     st->rows = 4;
67   if (st->columns < 12) st->columns = 12;
68
69   if (st->y > st->rows)    st->y = st->rows-1;
70   if (st->x > st->columns) st->x = st->columns-2;
71
72   if (st->right > st->columns) st->right = st->columns;
73   if (st->left > st->columns-20) st->left = st->columns-20;
74   if (st->left < 0) st->left = 0;
75
76   XClearWindow (st->dpy, st->window);
77 }
78
79
80 static void *
81 xjack_init (Display *dpy, Window window)
82 {
83   struct state *st = (struct state *) calloc (1, sizeof(*st));
84   XGCValues gcv;
85   char *fontname;
86
87   st->dpy = dpy;
88   st->window = window;
89   st->s = source;
90   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
91   fontname = get_string_resource (st->dpy, "font", "Font");
92
93   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
94
95   if (st->xgwa.width <= 480)
96     fontname = "-*-courier-medium-r-*-*-*-180-*-*-m-*-*-*";
97
98   st->font = XLoadQueryFont (st->dpy, fontname);
99
100   if (!st->font)
101     st->font = XLoadQueryFont (st->dpy, "-*-*-medium-r-*-*-*-240-*-*-m-*-*-*");
102   if (!st->font)
103     st->font = XLoadQueryFont (st->dpy,
104                                "-*-courier-medium-r-*-*-*-180-*-*-m-*-*-*");
105   if (!st->font)
106     st->font = XLoadQueryFont (st->dpy, "-*-*-*-r-*-*-*-240-*-*-m-*-*-*");
107   if (!st->font)
108     {
109       fprintf(stderr, "no big fixed-width font like \"%s\"\n", fontname);
110       exit(1);
111     }
112
113   gcv.font = st->font->fid;
114   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
115                                        "foreground", "Foreground");
116   gcv.background = get_pixel_resource (st->dpy, st->xgwa.colormap,
117                                        "background", "Background");
118   st->gc = XCreateGC (st->dpy, st->window,
119                       (GCFont | GCForeground | GCBackground), &gcv);
120
121   st->char_width = 
122     (st->font->per_char
123      ? st->font->per_char['n'-st->font->min_char_or_byte2].rbearing
124      : st->font->min_bounds.rbearing);
125   st->line_height = st->font->ascent + st->font->descent + 1;
126
127   xjack_reshape (dpy, window, st, st->xgwa.width, st->xgwa.height);
128
129   st->left = 0xFF & (random() % ((st->columns / 2)+1));
130   st->right = st->left + (0xFF & (random() % (st->columns - st->left)
131                                   + 10));
132   if (st->right < st->left + 10) st->right = st->left + 10;
133   if (st->right > st->columns)   st->right = st->columns;
134
135   st->x = st->left;
136   st->y = 0;
137
138   if (st->xgwa.width > 200 && st->xgwa.height > 200)
139     st->hspace = st->vspace = 40;
140
141   return st;
142 }
143
144 static unsigned long
145 xjack_scroll (struct state *st)
146 {
147   st->break_para = 0;
148   if (st->subscrolling)
149     {
150       int inc = st->line_height / 7;
151       XCopyArea (st->dpy, st->window, st->window, st->gc,
152                  0, inc,
153                  st->xgwa.width, st->xgwa.height - inc,
154                  0, 0);
155
156       /* See? It's OK. He saw it on the television. */
157       XClearArea (st->dpy, st->window,
158                   0, st->xgwa.height - inc, st->xgwa.width, inc,
159                   False);
160
161       st->subscrolling -= inc;
162       if (st->subscrolling <= 0)
163         st->subscrolling = 0;
164       if (st->subscrolling == 0)
165         {
166           if (st->scrolling > 0)
167             st->scrolling--;
168           st->y--;
169         }
170       return st->delay / 1000;
171     }
172   else if (st->scrolling)
173     st->subscrolling = st->line_height;
174
175   if (st->y < 0)
176     st->y = 0;
177   else if (st->y >= st->rows-1)
178     st->y = st->rows-1;
179
180   return st->delay;
181 }
182
183 static unsigned long
184 xjack_pine (struct state *st)
185 {
186   /* See also http://catalog.com/hopkins/unix-haters/login.html */
187   const char *n1 = "NFS server overlook not responding, still trying...";
188   const char *n2 = "NFS server overlook ok.";
189   int prev = st->pining;
190
191   if (!st->pining)
192     st->pining = 1 + (random() % 3);
193
194   if (prev)
195     while (*n2)
196       {
197         XDrawString (st->dpy, st->window, st->gc,
198                      (st->x * st->char_width) + st->hspace,
199                      ((st->y * st->line_height) + st->vspace
200                       + st->font->ascent),
201                      (char *) n2, 1);
202         st->x++;
203         if (st->x >= st->columns) st->x = 0, st->y++;
204         n2++;
205       }
206   st->y++;
207   st->x = 0;
208   st->pining--;
209
210   if (st->pining)
211     while (*n1)
212       {
213         XDrawString (st->dpy, st->window, st->gc,
214                      (st->x * st->char_width) + st->hspace,
215                      ((st->y * st->line_height) + st->vspace
216                       + st->font->ascent),
217                      (char *) n1, 1);
218         st->x++;
219         if (st->x >= st->columns) st->x = 0, st->y++;
220         n1++;
221       }
222
223   return 5000000;
224 }
225
226
227 static unsigned long
228 xjack_draw (Display *dpy, Window window, void *closure)
229 {
230   struct state *st = (struct state *) closure;
231   int this_delay = st->delay;
232   int word_length = 0;
233   const char *s2;
234
235   if (st->scrolling)
236     return xjack_scroll (st);
237   if (st->pining)
238     return xjack_pine (st);
239
240   for (s2 = st->s; *s2 && *s2 != ' '; s2++)
241     word_length++;
242
243   if (st->break_para ||
244       (*st->s != ' ' &&
245        (st->x + word_length) >= st->right))
246     {
247       st->x = st->left;
248       st->y++;
249
250       if (st->break_para)
251         st->y++;
252
253       st->break_para = 0;
254
255       if (st->mode == 1 || st->mode == 2)
256         {
257           /* 1 = left margin goes southwest; 2 = southeast */
258           st->left += (st->mode == 1 ? 1 : -1);
259           if (st->left >= st->right - 10)
260             {
261               if ((st->right < (st->columns - 10)) && (random() & 1))
262                 st->right += (0xFF & (random() % (st->columns - st->right)));
263               else
264                 st->mode = 2;
265             }
266           else if (st->left <= 0)
267             {
268               st->left = 0;
269               st->mode = 1;
270             }
271         }
272       else if (st->mode == 3 || st->mode == 4)
273         {
274           /* 3 = right margin goes southeast; 4 = southwest */
275           st->right += (st->mode == 3 ? 1 : -1);
276           if (st->right >= st->columns)
277             {
278               st->right = st->columns;
279               st->mode = 4;
280             }
281           else if (st->right <= st->left + 10)
282             st->mode = 3;
283         }
284
285       if (st->y >= st->rows-1)  /* bottom of page */
286         {
287 # if 0    /* Nah, this looks bad. */
288
289           /* scroll by 1-5 lines */
290           st->scrolling = (random() % 5 ? 0 : (0xFF & (random() % 5))) + 1;
291
292           if (st->break_para)
293             st->scrolling++;
294
295           /* but sometimes scroll by a whole page */
296           if (0 == (random() % 100))
297             st->scrolling += st->rows;
298 # else
299           st->scrolling = 1;
300 # endif
301
302           return xjack_scroll (st);
303         }
304     }
305
306   if (*st->s != ' ')
307     {
308       char c = *st->s;
309       int xshift = 0, yshift = 0;
310       if (0 == random() % 50)
311         {
312           xshift = random() % ((st->char_width / 3) + 1);      /* mis-strike */
313           yshift = random() % ((st->line_height / 6) + 1);
314           if (0 == (random() % 3))
315             yshift *= 2;
316           if (random() & 1)
317             xshift = -xshift;
318           if (random() & 1)
319             yshift = -yshift;
320         }
321
322       if (0 == (random() % 250))        /* introduce adjascent-key typo */
323         {
324           static const char * const typo[] = {
325             "asqw", "ASQW", "bgvhn", "cxdfv", "dserfcx", "ewsdrf",
326             "Jhuikmn", "kjiol,m", "lkop;.,", "mnjk,", "nbhjm", "oiklp09",
327             "pol;(-0", "redft54", "sawedxz", "uyhji87", "wqase32",
328             "yuhgt67", ".,l;/", 0 };
329           int i = 0;
330           while (typo[i] && typo[i][0] != c)
331             i++;
332           if (typo[i])
333             c = typo[i][0xFF & ((random() % strlen(typo[i]+1)) + 1)];
334         }
335
336       /* caps typo */
337       if (c >= 'a' && c <= 'z' && (st->caps || 0 == (random() % 350)))
338         {
339           c -= ('a'-'A');
340           if (c == 'O' && random() & 1)
341             c = '0';
342         }
343
344     OVERSTRIKE:
345       XDrawString (st->dpy, st->window, st->gc,
346                    (st->x * st->char_width) + st->hspace + xshift,
347                    ((st->y * st->line_height) + st->vspace + st->font->ascent
348                     + yshift),
349                    &c, 1);
350       if (xshift == 0 && yshift == 0 && (0 == (random() & 3000)))
351         {
352           if (random() & 1)
353             xshift--;
354           else
355             yshift--;
356           goto OVERSTRIKE;
357         }
358
359       if ((tolower(c) != tolower(*st->s))
360           ? (0 == (random() % 10))      /* backup to correct */
361           : (0 == (random() % 400)))    /* fail to advance */
362         {
363           st->x--;
364           st->s--;
365           if (st->delay)
366             st->delay += (0xFFFF & (st->delay + 
367                                     (random() % (st->delay * 10))));
368         }
369     }
370
371   st->x++;
372   st->s++;
373
374   if (0 == random() % 200)
375     {
376       if (random() & 1 && st->s != source)
377         st->s--;        /* duplicate character */
378       else if (*st->s)
379         st->s++;        /* skip character */
380     }
381
382   if (*st->s == 0)
383     {
384       st->sentences++;
385       st->caps = (0 == random() % 40);  /* capitalize sentence */
386
387       if (0 == (random() % 10) ||       /* randomly break paragraph */
388           (st->mode == 0 &&
389            ((0 == (random() % 10)) || st->sentences > 20)))
390         {
391           st->break_para = True;
392           st->sentences = 0;
393           st->paras++;
394
395           if (random() & 1)             /* mode=0 50% of the time */
396             st->mode = 0;
397           else
398             st->mode = (0xFF & (random() % 5));
399
400           if (0 == (random() % 2))      /* re-pick margins */
401             {
402               st->left = 0xFF & (random() % (st->columns / 3));
403               st->right = (st->columns -
404                            (0xFF & (random() % (st->columns / 3))));
405
406               if (0 == random() % 3)    /* sometimes be wide */
407                 st->right = st->left + ((st->right - st->left) / 2);
408             }
409
410           if (st->right - st->left <= 10)       /* introduce sanity */
411             {
412               st->left = 0;
413               st->right = st->columns;
414             }
415
416           if (st->right - st->left > 50)        /* if wide, shrink and move */
417             {
418               st->left += (0xFF & (random() % ((st->columns - 50) + 1)));
419               st->right = st->left + (0xFF & ((random() % 40) + 10));
420             }
421
422           /* oh, gag. */
423           if (st->mode == 0 &&
424               st->right - st->left < 25 &&
425               st->columns > 40)
426             {
427               st->right += 20;
428               if (st->right > st->columns)
429                 st->left -= (st->right - st->columns);
430             }
431
432           if (st->right - st->left < 5)
433             st->left = st->right - 5;
434           if (st->left < 0)
435             st->left = 0;
436           if (st->right - st->left < 5)
437             st->right = st->left + 5;
438         }
439       st->s = source;
440     }
441
442   if (st->delay)
443     {
444       if (0 == random() % 3)
445         this_delay += (0xFFFFFF & ((random() % (st->delay * 5)) + 1));
446
447       if (st->break_para)
448         this_delay += (0xFFFFFF & ((random() % (st->delay * 15)) + 1));
449     }
450
451   if (st->paras > 5 &&
452       (0 == (random() % 1000)) &&
453       st->y < st->rows-2)
454     return xjack_pine (st);
455
456   return this_delay;
457 }
458
459 static Bool
460 xjack_event (Display *dpy, Window window, void *closure, XEvent *event)
461 {
462   struct state *st = (struct state *) closure;
463   if (event->xany.type == ButtonPress)
464     {
465       st->scrolling++;
466       return True;
467     }
468
469   return False;
470 }
471
472 static void
473 xjack_free (Display *dpy, Window window, void *closure)
474 {
475   struct state *st = (struct state *) closure;
476   free (st);
477 }
478
479
480 static const char *xjack_defaults [] = {
481   ".background:         #FFF0B4",
482   ".foreground:         #000000",
483   "*fpsSolid:           true",
484 #ifdef HAVE_COCOA
485   ".font:               American Typewriter 24",
486 #else
487   ".font:               -*-courier-medium-r-*-*-*-240-*-*-m-*-*-*",
488 #endif
489   "*delay:              50000",
490   0
491 };
492
493 static XrmOptionDescRec xjack_options [] = {
494   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
495   { "-font",            ".font",        XrmoptionSepArg, 0 },
496   { 0, 0, 0, 0 }
497 };
498
499 XSCREENSAVER_MODULE ("XJack", xjack)