http://ftp.x.org/contrib/applications/xscreensaver-3.24.tar.gz
[xscreensaver] / hacks / xteevee.c
1 /*****************************************************************************
2  *                                                                           *
3  * xteevee -- TV good... TV good...                                          *
4  *                                                                           *
5  * Copyright (c) 1999 Greg Knauss (greg@eod.com)                             *
6  *                                                                           *
7  * Permission to use, copy, modify, distribute, and sell this software and   *
8  * its documentation for any purpose is hereby granted without fee, provided *
9  * that the above copyright notice appear in all copies and that both that   *
10  * copyright notice and this permission notice appear in supporting          *
11  * documentation.  No representations are made about the suitability of this *
12  * software for any purpose.  It is provided "as is" without express or      *
13  * implied warranty.                                                         *
14  *                                                                           *
15  *****************************************************************************/
16
17
18 /* Changelog *****************************************************************
19
20         1.0.0   19991119        Initial release
21
22 */
23
24
25 /* Includes ******************************************************************/
26 #include "screenhack.h"
27 #include <X11/Xutil.h>
28
29
30 /* Defines *******************************************************************/
31 #define XTEEVEE_NAME                    "XTeeVee"
32 #define XTEEVEE_MODE_EXCLUDE            0
33 #define XTEEVEE_MODE_INCLUDE_IMPLICIT   1
34 #define XTEEVEE_MODE_INCLUDE_EXPLICIT   2
35 #define XTEEVEE_ARG_STATIC              "static"
36 #define XTEEVEE_ARG_STATIC_SIGNAL       "staticSignal"
37 #define XTEEVEE_ARG_ROLL                "roll"
38 #define XTEEVEE_ARG_BARS                "bars"
39 #define XTEEVEE_ARG_CYCLE               "cycle"
40 #define XTEEVEE_ARG_DELAY_MODE          "delayMode"
41 #define XTEEVEE_ARG_DELAY_BETWEEN       "delayBetween"
42 #define XTEEVEE_STATIC_COLOR_COUNT      6
43 #define XTEEVEE_STATIC_TILE_COUNT       16
44 #define XTEEVEE_BARS_COLOR_TOP_COUNT    7
45 #define XTEEVEE_BARS_COLOR_BOTTOM_COUNT 5
46
47
48 /* Globals *******************************************************************/
49 char *progclass  = XTEEVEE_NAME;
50 char *defaults[] =
51 {
52         "*" XTEEVEE_ARG_STATIC        ": true",
53         "*" XTEEVEE_ARG_STATIC_SIGNAL ": 50",
54         "*" XTEEVEE_ARG_ROLL          ": true",
55         "*" XTEEVEE_ARG_BARS          ": true",
56         "*" XTEEVEE_ARG_CYCLE         ": true",
57         "*" XTEEVEE_ARG_DELAY_MODE    ": 30",
58         "*" XTEEVEE_ARG_DELAY_BETWEEN ": 3",
59         NULL
60 };
61 XrmOptionDescRec options[] =
62 {
63  { "-"    XTEEVEE_ARG_STATIC,"._" XTEEVEE_ARG_STATIC,XrmoptionNoArg,"true" },
64  { "-no-" XTEEVEE_ARG_STATIC,"."  XTEEVEE_ARG_STATIC,XrmoptionNoArg,"false" },
65  { "-"    XTEEVEE_ARG_ROLL  ,"._" XTEEVEE_ARG_ROLL  ,XrmoptionNoArg,"true" },
66  { "-no-" XTEEVEE_ARG_ROLL  ,"."  XTEEVEE_ARG_ROLL  ,XrmoptionNoArg,"false" },
67  { "-"    XTEEVEE_ARG_BARS  ,"._" XTEEVEE_ARG_BARS  ,XrmoptionNoArg,"true" },
68  { "-no-" XTEEVEE_ARG_BARS  ,"."  XTEEVEE_ARG_BARS  ,XrmoptionNoArg,"false" },
69  { "-"    XTEEVEE_ARG_CYCLE ,"."  XTEEVEE_ARG_CYCLE ,XrmoptionNoArg,"true" },
70  { "-no-" XTEEVEE_ARG_CYCLE ,"."  XTEEVEE_ARG_CYCLE ,XrmoptionNoArg,"false" },
71  { NULL                     ,NULL                   ,0             ,NULL }
72 };
73
74
75 /* Functions *****************************************************************/
76
77 /* Get the forground pixel ================================================= */
78 void xteevee_FreeColorForeground(Display* x_Disp,XWindowAttributes* x_WinAttr,
79       GC x_Gc)
80 {
81         XGCValues x_GcVal;
82
83         if (XGetGCValues(x_Disp,x_Gc,GCForeground,&x_GcVal) != 0)
84         {
85                 XFreeColors(x_Disp,x_WinAttr->colormap,&x_GcVal.foreground,1,
86                  0);
87         }
88 }
89
90 /* Static ================================================================== */
91 void xteevee_Static(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
92       time_t hack_Time,Pixmap hack_Pm)
93 {
94         GC        x_Gc[XTEEVEE_STATIC_COLOR_COUNT];
95         unsigned long pixels[XTEEVEE_STATIC_COLOR_COUNT];
96         XImage   *xim = 0;
97         char     *orig_bits = 0;
98         XGCValues x_GcVal;
99         int       signal_Strength;
100         XColor    color_Color;
101         int       color_Index;
102         int       tile_Index;
103         Pixmap    tile_Tile[XTEEVEE_STATIC_TILE_COUNT];
104         int       tile_X;
105         int       tile_Y;
106         int       tile_Width;
107         int       tile_Height;
108         char      tile_Used[XTEEVEE_STATIC_TILE_COUNT/2+1];
109         int       tile_Selected;
110
111         /* Get any extra arguments */
112         signal_Strength = get_integer_resource(XTEEVEE_ARG_STATIC_SIGNAL,
113          "Integer");
114
115         /* Build the GCs */
116         color_Color.flags = DoRed|DoGreen|DoBlue;
117         for (color_Index = 0;color_Index < XTEEVEE_STATIC_COLOR_COUNT;
118          color_Index++)
119         {
120                 color_Color.red = color_Color.green = color_Color.blue =
121                  (((double)color_Index+1)/XTEEVEE_STATIC_COLOR_COUNT)*65535;
122                 if (XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color) == 0)
123                 {
124                         /* NOTE: I have no idea what to do here.  Why would
125                                  this fail? */
126                 }
127                 pixels[color_Index] = color_Color.pixel;
128                 x_GcVal.foreground = color_Color.pixel;
129                 x_Gc[color_Index] = XCreateGC(x_Disp,x_Win,GCForeground,
130                  &x_GcVal);
131         }
132
133         /* Build the tiles */
134         for (tile_Index = 0;tile_Index < XTEEVEE_STATIC_TILE_COUNT;
135          tile_Index++)
136         {
137                 if (signal_Strength == 0)
138                 {
139                         /* NOTE: Checking XQueryBestTile() returns tiles that
140                                  are too regular -- you can see patterns
141                                  emerge. */
142                         tile_Width = (random()%128)+64;
143                         tile_Height = (random()%128)+64;
144                 }
145                 else
146                 {
147                         tile_Width = x_WinAttr->width;
148                         tile_Height = x_WinAttr->height;
149                 }
150                 tile_Tile[tile_Index] = XCreatePixmap(x_Disp,x_Win,tile_Width,
151                  tile_Height,x_WinAttr->depth);
152                 XCopyArea(x_Disp,hack_Pm,tile_Tile[tile_Index],x_Gc[0],0,0,
153                  x_WinAttr->width,x_WinAttr->height,0,0);
154
155                 if (signal_Strength == 0)
156                   {
157                     if (!xim)
158                       {
159                         xim = XCreateImage (x_Disp, x_WinAttr->visual,
160                                             x_WinAttr->depth,
161                                             (x_WinAttr->depth == 1
162                                              ? XYPixmap : ZPixmap),
163                                             0, 0,
164                                             x_WinAttr->width,
165                                             x_WinAttr->height,
166                                             8, 0);
167                         
168                         xim->data = (char *) malloc (xim->bytes_per_line *
169                                                      xim->height);
170                       }
171                   }
172                 else if (xim)
173                   {
174                     memcpy (xim->data, orig_bits,
175                             xim->bytes_per_line * xim->height);
176                   }
177                 else
178                   {
179                     xim = XGetImage (x_Disp, tile_Tile[tile_Index], 0, 0,
180                                      x_WinAttr->width, x_WinAttr->height, ~0L,
181                                      (x_WinAttr->depth == 1
182                                       ? XYPixmap : ZPixmap));
183                     orig_bits = (char *) malloc (xim->bytes_per_line *
184                                                  xim->height);
185                     memcpy (orig_bits, xim->data,
186                             xim->bytes_per_line * xim->height);
187                   }
188
189                 for (tile_Y = tile_Height-1;tile_Y >= 0;tile_Y--)
190                   for (tile_X = tile_Width-1;tile_X >= 0;tile_X--)
191                     if (random()%100 > signal_Strength)
192                       XPutPixel(xim,tile_X,tile_Y,
193                                 pixels[random()%XTEEVEE_STATIC_COLOR_COUNT]);
194                 XPutImage(x_Disp,tile_Tile[tile_Index],x_Gc[0],xim,
195                           0,0,0,0,x_WinAttr->width,x_WinAttr->height);
196         }
197
198         if (xim) XDestroyImage (xim);
199         if (orig_bits) free (orig_bits);
200
201         /* Go! */
202         memset(tile_Used,-1,sizeof(tile_Used));
203         if (hack_Time > 0)
204         {
205                 hack_Time += time(NULL);
206         }
207         while ((time(NULL) < hack_Time) || (hack_Time == 0))
208         {
209                 /* Pick a tile */
210                 do
211                 {
212                         tile_Selected = random()%XTEEVEE_STATIC_TILE_COUNT;
213                         for (tile_Index = 0;tile_Index < sizeof(tile_Used);
214                          tile_Index++)
215                         {
216                                 if (tile_Used[tile_Index] == tile_Selected)
217                                 {
218                                         tile_Selected = -1;
219                                         break;
220                                 }
221                         }
222                 } while (tile_Selected == -1);
223                 memmove(tile_Used,tile_Used+1,sizeof(tile_Used)-1);
224                 tile_Used[sizeof(tile_Used)-1] = tile_Selected;
225
226                 /* Set it */
227                 XSetWindowBackgroundPixmap(x_Disp,x_Win,
228                  tile_Tile[tile_Selected]);
229                 XClearWindow(x_Disp,x_Win);
230
231                 XSync(x_Disp,0);
232                 screenhack_handle_events(x_Disp);
233                 usleep(25000);
234         }
235
236         /* Free everything */
237         for (color_Index = 0;color_Index < XTEEVEE_STATIC_COLOR_COUNT;
238          color_Index++)
239         {
240                 xteevee_FreeColorForeground(x_Disp,x_WinAttr,
241                  x_Gc[color_Index]);
242                 XFreeGC(x_Disp,x_Gc[color_Index]);
243         }
244
245         for (tile_Index = 0;tile_Index < XTEEVEE_STATIC_TILE_COUNT;
246          tile_Index++)
247         {
248                 XFreePixmap(x_Disp, tile_Tile[tile_Index]);
249         }
250 }
251
252 /* Vertical Roll =========================================================== */
253 void xteevee_Roll(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
254       time_t hack_Time,Pixmap hack_Pm)
255 {
256         GC        x_Gc;
257         XGCValues x_GcVal;
258         int       roll_Y = 0;
259         int       roll_Speed = 0;
260         int       blank_Height = x_WinAttr->height/10;
261
262         /* Build the GC */
263         x_GcVal.foreground = BlackPixel(x_Disp,0);
264         x_GcVal.subwindow_mode = IncludeInferiors;
265         x_Gc = XCreateGC(x_Disp,x_Win,GCForeground|GCSubwindowMode,&x_GcVal);
266
267         /* Go! */
268         if (hack_Time > 0)
269         {
270                 hack_Time += time(NULL);
271         }
272         while ((roll_Y > 0) || ((time(NULL) < hack_Time) || (hack_Time == 0)))
273         {
274                 if (roll_Y > blank_Height)
275                 {
276                         XCopyArea(x_Disp,hack_Pm,x_Win,x_Gc,
277                          0,x_WinAttr->height-(roll_Y-blank_Height)-1,
278                          x_WinAttr->width,roll_Y-blank_Height,
279                          0,0);
280                 }
281                 XFillRectangle(x_Disp,x_Win,x_Gc,
282                  0,roll_Y-blank_Height,
283                  x_WinAttr->width,blank_Height);
284                 if (roll_Y < x_WinAttr->height)
285                 {
286                         XCopyArea(x_Disp,hack_Pm,x_Win,x_Gc,
287                          0,0,
288                          x_WinAttr->width,x_WinAttr->height-roll_Y,
289                          0,roll_Y);
290                 }
291
292                 roll_Y += roll_Speed/2;
293                 if (roll_Speed < 50)
294                 {
295                         roll_Speed++;
296                 }
297                 if (roll_Y > x_WinAttr->height+blank_Height)
298                 {
299                         roll_Y = 0;
300                         roll_Speed = 0;
301                 }
302
303                 XSync(x_Disp,0);
304                 sleep(0);
305                 screenhack_handle_events(x_Disp);
306         }
307
308         /* Free everything */
309         XFreeGC(x_Disp,x_Gc);
310 }
311
312 /* Color-Bars Test Pattern ================================================= */
313 void xteevee_Bars(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
314       time_t hack_Time,Pixmap hack_Pm)
315 {
316         GC        x_GcTop[XTEEVEE_BARS_COLOR_TOP_COUNT];
317         GC        x_GcBottom[XTEEVEE_BARS_COLOR_BOTTOM_COUNT];
318         XGCValues x_GcVal;
319         int       color_Index;
320         XColor    color_Color;
321         char*     color_ColorTop[] =
322         {
323                 "grey",
324                 "yellow",
325                 "light blue",
326                 "green",
327                 "orange",
328                 "red",
329                 "purple"
330         };
331         char*     color_ColorBottom[] =
332         {
333                 "black",
334                 "white",
335                 "black",
336                 "black",
337                 "black"
338         };
339
340         /* Build the GCs */
341         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
342          color_Index++)
343         {
344                 if (XParseColor(x_Disp,x_WinAttr->colormap,
345                  color_ColorTop[color_Index],&color_Color) == 0)
346                 {
347                         /* NOTE: Um, badness? */
348                 }
349                 if (XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color) == 0)
350                 {
351                         /* NOTE: More badness? */
352                 }
353                 x_GcVal.foreground = color_Color.pixel;
354                 x_GcTop[color_Index] =
355                  XCreateGC(x_Disp,x_Win,GCForeground,&x_GcVal);
356         }
357         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
358          color_Index++)
359         {
360                 if (XParseColor(x_Disp,x_WinAttr->colormap,
361                  color_ColorBottom[color_Index],&color_Color) == 0)
362                 {
363                         /* NOTE: Um, badness? */
364                 }
365                 if (XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color) == 0)
366                 {
367                         /* NOTE: More badness? */
368                 }
369                 x_GcVal.foreground = color_Color.pixel;
370                 x_GcBottom[color_Index] =
371                  XCreateGC(x_Disp,x_Win,GCForeground,&x_GcVal);
372         }
373
374         /* Draw color-bar test pattern */
375         XClearWindow(x_Disp,x_Win);
376         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
377          color_Index++)
378         {
379                 XFillRectangle(x_Disp,x_Win,x_GcTop[color_Index],
380                  ((x_WinAttr->width/XTEEVEE_BARS_COLOR_TOP_COUNT)+1)*
381                  color_Index,
382                  0,
383                  (x_WinAttr->width/XTEEVEE_BARS_COLOR_TOP_COUNT)+1,
384                  (x_WinAttr->height/5)*4);
385         }
386         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
387          color_Index++)
388         {
389                 XFillRectangle(x_Disp,x_Win,x_GcBottom[color_Index],
390                  ((x_WinAttr->width/XTEEVEE_BARS_COLOR_BOTTOM_COUNT)+1)*
391                  color_Index,
392                  (x_WinAttr->height/5)*4,
393                  (x_WinAttr->width/XTEEVEE_BARS_COLOR_BOTTOM_COUNT)+1,
394                  x_WinAttr->height-(x_WinAttr->height/5)*4);
395         }
396
397         /* Go! */
398         if (hack_Time > 0)
399         {
400                 hack_Time += time(NULL);
401         }
402         while ((time(NULL) < hack_Time) || (hack_Time == 0))
403         {
404                 screenhack_handle_events(x_Disp);
405                 usleep(100000);
406         }
407
408         /* Free everything */
409         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
410          color_Index++)
411         {
412                 xteevee_FreeColorForeground(x_Disp,x_WinAttr,
413                  x_GcTop[color_Index]);
414                 XFreeGC(x_Disp,x_GcTop[color_Index]);
415         }
416         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
417          color_Index++)
418         {
419                 xteevee_FreeColorForeground(x_Disp,x_WinAttr,
420                  x_GcBottom[color_Index]);
421                 XFreeGC(x_Disp,x_GcBottom[color_Index]);
422         }
423 }
424
425 /* Standard XScreenSaver entry point ======================================= */
426 void screenhack(Display* x_Disp,Window x_Win)
427 {
428         XWindowAttributes x_WinAttr;
429         GC                x_Gc;
430         XGCValues         x_GcVal;
431         Pixmap            screen_Pm;
432         time_t            delay_Time;
433         int               delay_Mode;
434         int               delay_Between;
435         int               mode_Index;
436         int               mode_Count = 0;
437         int               mode_Total = 0;
438         char              mode_Arg[64+1];
439         int               mode_Min = XTEEVEE_MODE_INCLUDE_IMPLICIT;
440         struct
441         {
442                 char* mode_Arg;
443                 void  (*mode_Func)(Display* x_Disp,Window x_Win,
444                       XWindowAttributes* x_WinAttr,time_t hack_Time,
445                       Pixmap hack_Pm);
446                 int   mode_Flag;
447         } mode_Mode[] =
448         {
449                 { XTEEVEE_ARG_STATIC,xteevee_Static,XTEEVEE_MODE_EXCLUDE },
450                 { XTEEVEE_ARG_ROLL,  xteevee_Roll,  XTEEVEE_MODE_EXCLUDE },
451                 { XTEEVEE_ARG_BARS,  xteevee_Bars,  XTEEVEE_MODE_EXCLUDE },
452                 { NULL,              NULL,          -1 }
453         };
454
455         /* Grab the screen to give us time to do whatever we want */
456         XGetWindowAttributes(x_Disp,x_Win,&x_WinAttr);
457         grab_screen_image(x_WinAttr.screen,x_Win);
458         x_GcVal.subwindow_mode = IncludeInferiors;
459         x_Gc = XCreateGC(x_Disp,x_Win,GCSubwindowMode,&x_GcVal);
460         screen_Pm = XCreatePixmap(x_Disp,x_Win,x_WinAttr.width,
461          x_WinAttr.height,x_WinAttr.depth);
462         XCopyArea(x_Disp,x_Win,screen_Pm,x_Gc,0,0,x_WinAttr.width,
463          x_WinAttr.height,0,0);
464
465         /* Read the arguments */
466         delay_Mode = get_integer_resource(XTEEVEE_ARG_DELAY_MODE,"Integer");
467         delay_Between = get_integer_resource(XTEEVEE_ARG_DELAY_BETWEEN,
468          "Integer");
469         if (!get_boolean_resource(XTEEVEE_ARG_CYCLE,"Boolean"))
470         {
471                 delay_Mode = 0;
472         }
473         for (mode_Index = 0;mode_Mode[mode_Index].mode_Arg != NULL;
474          mode_Index++)
475         {
476                 if (get_boolean_resource(mode_Mode[mode_Index].mode_Arg,
477                  "Boolean") != 0)
478                 {
479                         mode_Mode[mode_Index].mode_Flag =
480                          XTEEVEE_MODE_INCLUDE_IMPLICIT;
481                         mode_Count++;
482                 }
483                 sprintf(mode_Arg,"_%s",mode_Mode[mode_Index].mode_Arg);
484                 if (get_boolean_resource(mode_Arg,"Boolean") != 0)
485                 {
486                         mode_Mode[mode_Index].mode_Flag =
487                          XTEEVEE_MODE_INCLUDE_EXPLICIT;
488                         mode_Min = XTEEVEE_MODE_INCLUDE_EXPLICIT;
489                         mode_Count++;
490                 }
491                 mode_Total++;
492         }
493         if (mode_Count == 0)
494         {
495                 fprintf(stderr,"%s: No modes selected!\n",XTEEVEE_NAME);
496                 return;
497         }
498
499         /* Cycle through various modes */
500         for (;;)
501         {
502                 /* Find a mode */
503                 do
504                 {
505                         mode_Index = random()%mode_Total;
506                 } while (mode_Mode[mode_Index].mode_Flag < mode_Min);
507
508                 /* Run the hack */
509                 mode_Mode[mode_Index].mode_Func(x_Disp,x_Win,&x_WinAttr,
510                  delay_Mode,screen_Pm);
511
512                 /* Restore the screen and wait */
513                 XCopyArea(x_Disp,screen_Pm,x_Win,x_Gc,0,0,x_WinAttr.width,
514                  x_WinAttr.height,0,0);
515                 delay_Time = time(NULL)+delay_Between;
516                 while (time(NULL) < delay_Time)
517                 {
518                         screenhack_handle_events(x_Disp);
519                         usleep(100000);
520                 }
521         }
522 }