http://ftp.x.org/contrib/applications/xscreensaver-3.23.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
246 /* Vertical Roll =========================================================== */
247 void xteevee_Roll(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
248       time_t hack_Time,Pixmap hack_Pm)
249 {
250         GC        x_Gc;
251         XGCValues x_GcVal;
252         int       roll_Y = 0;
253         int       roll_Speed = 0;
254         int       blank_Height = x_WinAttr->height/10;
255
256         /* Build the GC */
257         x_GcVal.foreground = BlackPixel(x_Disp,0);
258         x_GcVal.subwindow_mode = IncludeInferiors;
259         x_Gc = XCreateGC(x_Disp,x_Win,GCForeground|GCSubwindowMode,&x_GcVal);
260
261         /* Go! */
262         if (hack_Time > 0)
263         {
264                 hack_Time += time(NULL);
265         }
266         while ((roll_Y > 0) || ((time(NULL) < hack_Time) || (hack_Time == 0)))
267         {
268                 if (roll_Y > blank_Height)
269                 {
270                         XCopyArea(x_Disp,hack_Pm,x_Win,x_Gc,
271                          0,x_WinAttr->height-(roll_Y-blank_Height)-1,
272                          x_WinAttr->width,roll_Y-blank_Height,
273                          0,0);
274                 }
275                 XFillRectangle(x_Disp,x_Win,x_Gc,
276                  0,roll_Y-blank_Height,
277                  x_WinAttr->width,blank_Height);
278                 if (roll_Y < x_WinAttr->height)
279                 {
280                         XCopyArea(x_Disp,hack_Pm,x_Win,x_Gc,
281                          0,0,
282                          x_WinAttr->width,x_WinAttr->height-roll_Y,
283                          0,roll_Y);
284                 }
285
286                 roll_Y += roll_Speed/2;
287                 if (roll_Speed < 50)
288                 {
289                         roll_Speed++;
290                 }
291                 if (roll_Y > x_WinAttr->height+blank_Height)
292                 {
293                         roll_Y = 0;
294                         roll_Speed = 0;
295                 }
296
297                 XSync(x_Disp,0);
298                 sleep(0);
299                 screenhack_handle_events(x_Disp);
300         }
301
302         /* Free everything */
303         XFreeGC(x_Disp,x_Gc);
304 }
305
306 /* Color-Bars Test Pattern ================================================= */
307 void xteevee_Bars(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
308       time_t hack_Time,Pixmap hack_Pm)
309 {
310         GC        x_GcTop[XTEEVEE_BARS_COLOR_TOP_COUNT];
311         GC        x_GcBottom[XTEEVEE_BARS_COLOR_BOTTOM_COUNT];
312         XGCValues x_GcVal;
313         int       color_Index;
314         XColor    color_Color;
315         char*     color_ColorTop[] =
316         {
317                 "grey",
318                 "yellow",
319                 "light blue",
320                 "green",
321                 "orange",
322                 "red",
323                 "purple"
324         };
325         char*     color_ColorBottom[] =
326         {
327                 "black",
328                 "white",
329                 "black",
330                 "black",
331                 "black"
332         };
333
334         /* Build the GCs */
335         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
336          color_Index++)
337         {
338                 if (XParseColor(x_Disp,x_WinAttr->colormap,
339                  color_ColorTop[color_Index],&color_Color) == 0)
340                 {
341                         /* NOTE: Um, badness? */
342                 }
343                 if (XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color) == 0)
344                 {
345                         /* NOTE: More badness? */
346                 }
347                 x_GcVal.foreground = color_Color.pixel;
348                 x_GcTop[color_Index] =
349                  XCreateGC(x_Disp,x_Win,GCForeground,&x_GcVal);
350         }
351         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
352          color_Index++)
353         {
354                 if (XParseColor(x_Disp,x_WinAttr->colormap,
355                  color_ColorBottom[color_Index],&color_Color) == 0)
356                 {
357                         /* NOTE: Um, badness? */
358                 }
359                 if (XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color) == 0)
360                 {
361                         /* NOTE: More badness? */
362                 }
363                 x_GcVal.foreground = color_Color.pixel;
364                 x_GcBottom[color_Index] =
365                  XCreateGC(x_Disp,x_Win,GCForeground,&x_GcVal);
366         }
367
368         /* Draw color-bar test pattern */
369         XClearWindow(x_Disp,x_Win);
370         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
371          color_Index++)
372         {
373                 XFillRectangle(x_Disp,x_Win,x_GcTop[color_Index],
374                  ((x_WinAttr->width/XTEEVEE_BARS_COLOR_TOP_COUNT)+1)*
375                  color_Index,
376                  0,
377                  (x_WinAttr->width/XTEEVEE_BARS_COLOR_TOP_COUNT)+1,
378                  (x_WinAttr->height/5)*4);
379         }
380         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
381          color_Index++)
382         {
383                 XFillRectangle(x_Disp,x_Win,x_GcBottom[color_Index],
384                  ((x_WinAttr->width/XTEEVEE_BARS_COLOR_BOTTOM_COUNT)+1)*
385                  color_Index,
386                  (x_WinAttr->height/5)*4,
387                  (x_WinAttr->width/XTEEVEE_BARS_COLOR_BOTTOM_COUNT)+1,
388                  x_WinAttr->height-(x_WinAttr->height/5)*4);
389         }
390
391         /* Go! */
392         if (hack_Time > 0)
393         {
394                 hack_Time += time(NULL);
395         }
396         while ((time(NULL) < hack_Time) || (hack_Time == 0))
397         {
398                 screenhack_handle_events(x_Disp);
399                 usleep(100000);
400         }
401
402         /* Free everything */
403         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_TOP_COUNT;
404          color_Index++)
405         {
406                 xteevee_FreeColorForeground(x_Disp,x_WinAttr,
407                  x_GcTop[color_Index]);
408                 XFreeGC(x_Disp,x_GcTop[color_Index]);
409         }
410         for (color_Index = 0;color_Index < XTEEVEE_BARS_COLOR_BOTTOM_COUNT;
411          color_Index++)
412         {
413                 xteevee_FreeColorForeground(x_Disp,x_WinAttr,
414                  x_GcBottom[color_Index]);
415                 XFreeGC(x_Disp,x_GcBottom[color_Index]);
416         }
417 }
418
419 /* Standard XScreenSaver entry point ======================================= */
420 void screenhack(Display* x_Disp,Window x_Win)
421 {
422         XWindowAttributes x_WinAttr;
423         GC                x_Gc;
424         XGCValues         x_GcVal;
425         Pixmap            screen_Pm;
426         time_t            delay_Time;
427         int               delay_Mode;
428         int               delay_Between;
429         int               mode_Index;
430         int               mode_Count = 0;
431         int               mode_Total = 0;
432         char              mode_Arg[64+1];
433         int               mode_Min = XTEEVEE_MODE_INCLUDE_IMPLICIT;
434         struct
435         {
436                 char* mode_Arg;
437                 void  (*mode_Func)(Display* x_Disp,Window x_Win,
438                       XWindowAttributes* x_WinAttr,time_t hack_Time,
439                       Pixmap hack_Pm);
440                 int   mode_Flag;
441         } mode_Mode[] =
442         {
443                 { XTEEVEE_ARG_STATIC,xteevee_Static,XTEEVEE_MODE_EXCLUDE },
444                 { XTEEVEE_ARG_ROLL,  xteevee_Roll,  XTEEVEE_MODE_EXCLUDE },
445                 { XTEEVEE_ARG_BARS,  xteevee_Bars,  XTEEVEE_MODE_EXCLUDE },
446                 { NULL,              NULL,          -1 }
447         };
448
449         /* Grab the screen to give us time to do whatever we want */
450         XGetWindowAttributes(x_Disp,x_Win,&x_WinAttr);
451         grab_screen_image(x_WinAttr.screen,x_Win);
452         x_GcVal.subwindow_mode = IncludeInferiors;
453         x_Gc = XCreateGC(x_Disp,x_Win,GCSubwindowMode,&x_GcVal);
454         screen_Pm = XCreatePixmap(x_Disp,x_Win,x_WinAttr.width,
455          x_WinAttr.height,x_WinAttr.depth);
456         XCopyArea(x_Disp,x_Win,screen_Pm,x_Gc,0,0,x_WinAttr.width,
457          x_WinAttr.height,0,0);
458
459         /* Read the arguments */
460         delay_Mode = get_integer_resource(XTEEVEE_ARG_DELAY_MODE,"Integer");
461         delay_Between = get_integer_resource(XTEEVEE_ARG_DELAY_BETWEEN,
462          "Integer");
463         if (!get_boolean_resource(XTEEVEE_ARG_CYCLE,"Boolean"))
464         {
465                 delay_Mode = 0;
466         }
467         for (mode_Index = 0;mode_Mode[mode_Index].mode_Arg != NULL;
468          mode_Index++)
469         {
470                 if (get_boolean_resource(mode_Mode[mode_Index].mode_Arg,
471                  "Boolean") != 0)
472                 {
473                         mode_Mode[mode_Index].mode_Flag =
474                          XTEEVEE_MODE_INCLUDE_IMPLICIT;
475                         mode_Count++;
476                 }
477                 sprintf(mode_Arg,"_%s",mode_Mode[mode_Index].mode_Arg);
478                 if (get_boolean_resource(mode_Arg,"Boolean") != 0)
479                 {
480                         mode_Mode[mode_Index].mode_Flag =
481                          XTEEVEE_MODE_INCLUDE_EXPLICIT;
482                         mode_Min = XTEEVEE_MODE_INCLUDE_EXPLICIT;
483                         mode_Count++;
484                 }
485                 mode_Total++;
486         }
487         if (mode_Count == 0)
488         {
489                 fprintf(stderr,"%s: No modes selected!\n",XTEEVEE_NAME);
490                 return;
491         }
492
493         /* Cycle through various modes */
494         for (;;)
495         {
496                 /* Find a mode */
497                 do
498                 {
499                         mode_Index = random()%mode_Total;
500                 } while (mode_Mode[mode_Index].mode_Flag < mode_Min);
501
502                 /* Run the hack */
503                 mode_Mode[mode_Index].mode_Func(x_Disp,x_Win,&x_WinAttr,
504                  delay_Mode,screen_Pm);
505
506                 /* Restore the screen and wait */
507                 XCopyArea(x_Disp,screen_Pm,x_Win,x_Gc,0,0,x_WinAttr.width,
508                  x_WinAttr.height,0,0);
509                 delay_Time = time(NULL)+delay_Between;
510                 while (time(NULL) < delay_Time)
511                 {
512                         screenhack_handle_events(x_Disp);
513                         usleep(100000);
514                 }
515         }
516 }