df4ce24ad772f7f544040967f3492d9fdfe9a7d6
[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 "colorbars.h"
28 #include <X11/Xutil.h>
29
30
31 /* Defines *******************************************************************/
32 #define XTEEVEE_NAME                    "XTeeVee"
33 #define XTEEVEE_MODE_EXCLUDE            0
34 #define XTEEVEE_MODE_INCLUDE_IMPLICIT   1
35 #define XTEEVEE_MODE_INCLUDE_EXPLICIT   2
36 #define XTEEVEE_ARG_STATIC              "static"
37 #define XTEEVEE_ARG_STATIC_SIGNAL       "staticSignal"
38 #define XTEEVEE_ARG_ROLL                "roll"
39 #define XTEEVEE_ARG_BARS                "bars"
40 #define XTEEVEE_ARG_CYCLE               "cycle"
41 #define XTEEVEE_ARG_DELAY_MODE          "delayMode"
42 #define XTEEVEE_ARG_DELAY_BETWEEN       "delayBetween"
43 #define XTEEVEE_STATIC_COLOR_COUNT      6
44 #define XTEEVEE_STATIC_TILE_COUNT       16
45
46
47 /* Globals *******************************************************************/
48 char *progclass  = XTEEVEE_NAME;
49 char *defaults[] =
50 {
51         "*" XTEEVEE_ARG_STATIC        ": true",
52         "*" XTEEVEE_ARG_STATIC_SIGNAL ": 50",
53         "*" XTEEVEE_ARG_ROLL          ": true",
54         "*" XTEEVEE_ARG_BARS          ": true",
55         "*" XTEEVEE_ARG_CYCLE         ": true",
56         "*" XTEEVEE_ARG_DELAY_MODE    ": 30",
57         "*" XTEEVEE_ARG_DELAY_BETWEEN ": 3",
58         NULL
59 };
60 XrmOptionDescRec options[] =
61 {
62  { "-"    XTEEVEE_ARG_STATIC,"._" XTEEVEE_ARG_STATIC,XrmoptionNoArg,"true" },
63  { "-no-" XTEEVEE_ARG_STATIC,"."  XTEEVEE_ARG_STATIC,XrmoptionNoArg,"false" },
64  { "-"    XTEEVEE_ARG_ROLL  ,"._" XTEEVEE_ARG_ROLL  ,XrmoptionNoArg,"true" },
65  { "-no-" XTEEVEE_ARG_ROLL  ,"."  XTEEVEE_ARG_ROLL  ,XrmoptionNoArg,"false" },
66  { "-"    XTEEVEE_ARG_BARS  ,"._" XTEEVEE_ARG_BARS  ,XrmoptionNoArg,"true" },
67  { "-no-" XTEEVEE_ARG_BARS  ,"."  XTEEVEE_ARG_BARS  ,XrmoptionNoArg,"false" },
68  { "-"    XTEEVEE_ARG_CYCLE ,"."  XTEEVEE_ARG_CYCLE ,XrmoptionNoArg,"true" },
69  { "-no-" XTEEVEE_ARG_CYCLE ,"."  XTEEVEE_ARG_CYCLE ,XrmoptionNoArg,"false" },
70  { NULL                     ,NULL                   ,0             ,NULL }
71 };
72
73
74 /* Functions *****************************************************************/
75
76 /* Get the forground pixel ================================================= */
77 void xteevee_FreeColorForeground(Display* x_Disp,XWindowAttributes* x_WinAttr,
78       GC x_Gc)
79 {
80         XGCValues x_GcVal;
81
82         if (XGetGCValues(x_Disp,x_Gc,GCForeground,&x_GcVal) != 0)
83         {
84                 XFreeColors(x_Disp,x_WinAttr->colormap,&x_GcVal.foreground,1,
85                  0);
86         }
87 }
88
89 /* Static ================================================================== */
90 void xteevee_Static(Display* x_Disp,Window x_Win,XWindowAttributes* x_WinAttr,
91       time_t hack_Time,Pixmap hack_Pm)
92 {
93         GC        x_Gc[XTEEVEE_STATIC_COLOR_COUNT];
94         unsigned long pixels[XTEEVEE_STATIC_COLOR_COUNT];
95         XImage   *xim = 0;
96         char     *orig_bits = 0;
97         XGCValues x_GcVal;
98         int       signal_Strength;
99         XColor    color_Color;
100         int       color_Index;
101         int       tile_Index;
102         Pixmap    tile_Tile[XTEEVEE_STATIC_TILE_COUNT];
103         int       tile_X;
104         int       tile_Y;
105         int       tile_Width;
106         int       tile_Height;
107         char      tile_Used[XTEEVEE_STATIC_TILE_COUNT/2+1];
108         int       tile_Selected;
109
110         /* Get any extra arguments */
111         signal_Strength = get_integer_resource(XTEEVEE_ARG_STATIC_SIGNAL,
112          "Integer");
113
114         /* Build the GCs */
115         color_Color.flags = DoRed|DoGreen|DoBlue;
116         for (color_Index = 0;color_Index < XTEEVEE_STATIC_COLOR_COUNT;
117          color_Index++)
118         {
119                 color_Color.red = color_Color.green = color_Color.blue =
120                  (((double)color_Index+1)/XTEEVEE_STATIC_COLOR_COUNT)*65535;
121                 if (!x_WinAttr->colormap ||
122                     !XAllocColor(x_Disp,x_WinAttr->colormap,&color_Color))
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                 usleep(50000);
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   draw_colorbars (x_Disp, x_Win, 0, 0, x_WinAttr->width, x_WinAttr->height);
317
318   /* Go! */
319   if (hack_Time > 0)
320     {
321       hack_Time += time(NULL);
322     }
323   while ((time(NULL) < hack_Time) || (hack_Time == 0))
324     {
325       screenhack_handle_events(x_Disp);
326       usleep(100000);
327     }
328 }
329
330
331 /* Standard XScreenSaver entry point ======================================= */
332 void screenhack(Display* x_Disp,Window x_Win)
333 {
334         XWindowAttributes x_WinAttr;
335         GC                x_Gc;
336         XGCValues         x_GcVal;
337         Pixmap            screen_Pm;
338         time_t            delay_Time;
339         int               delay_Mode;
340         int               delay_Between;
341         int               mode_Index;
342         int               mode_Count = 0;
343         int               mode_Total = 0;
344         char              mode_Arg[64+1];
345         int               mode_Min = XTEEVEE_MODE_INCLUDE_IMPLICIT;
346         struct
347         {
348                 char* mode_Arg;
349                 void  (*mode_Func)(Display* x_Disp,Window x_Win,
350                       XWindowAttributes* x_WinAttr,time_t hack_Time,
351                       Pixmap hack_Pm);
352                 int   mode_Flag;
353         } mode_Mode[] =
354         {
355                 { XTEEVEE_ARG_STATIC,xteevee_Static,XTEEVEE_MODE_EXCLUDE },
356                 { XTEEVEE_ARG_ROLL,  xteevee_Roll,  XTEEVEE_MODE_EXCLUDE },
357                 { XTEEVEE_ARG_BARS,  xteevee_Bars,  XTEEVEE_MODE_EXCLUDE },
358                 { NULL,              NULL,          -1 }
359         };
360
361         /* Grab the screen to give us time to do whatever we want */
362         XGetWindowAttributes(x_Disp,x_Win,&x_WinAttr);
363         grab_screen_image(x_WinAttr.screen,x_Win);
364
365         x_GcVal.subwindow_mode = IncludeInferiors;
366         x_Gc = XCreateGC(x_Disp,x_Win,GCSubwindowMode,&x_GcVal);
367         screen_Pm = XCreatePixmap(x_Disp,x_Win,x_WinAttr.width,
368          x_WinAttr.height,x_WinAttr.depth);
369         XCopyArea(x_Disp,x_Win,screen_Pm,x_Gc,0,0,x_WinAttr.width,
370          x_WinAttr.height,0,0);
371
372         /* Read the arguments */
373         delay_Mode = get_integer_resource(XTEEVEE_ARG_DELAY_MODE,"Integer");
374         delay_Between = get_integer_resource(XTEEVEE_ARG_DELAY_BETWEEN,
375          "Integer");
376         if (!get_boolean_resource(XTEEVEE_ARG_CYCLE,"Boolean"))
377         {
378                 delay_Mode = 0;
379         }
380         for (mode_Index = 0;mode_Mode[mode_Index].mode_Arg != NULL;
381          mode_Index++)
382         {
383                 if (get_boolean_resource(mode_Mode[mode_Index].mode_Arg,
384                  "Boolean") != 0)
385                 {
386                         mode_Mode[mode_Index].mode_Flag =
387                          XTEEVEE_MODE_INCLUDE_IMPLICIT;
388                         mode_Count++;
389                 }
390                 sprintf(mode_Arg,"_%s",mode_Mode[mode_Index].mode_Arg);
391                 if (get_boolean_resource(mode_Arg,"Boolean") != 0)
392                 {
393                         mode_Mode[mode_Index].mode_Flag =
394                          XTEEVEE_MODE_INCLUDE_EXPLICIT;
395                         mode_Min = XTEEVEE_MODE_INCLUDE_EXPLICIT;
396                         mode_Count++;
397                 }
398                 mode_Total++;
399         }
400         if (mode_Count == 0)
401         {
402                 fprintf(stderr,"%s: No modes selected!\n",XTEEVEE_NAME);
403                 return;
404         }
405
406         /* Cycle through various modes */
407         for (;;)
408         {
409                 /* Find a mode */
410                 do
411                 {
412                         mode_Index = random()%mode_Total;
413                 } while (mode_Mode[mode_Index].mode_Flag < mode_Min);
414
415                 /* Run the hack */
416                 mode_Mode[mode_Index].mode_Func(x_Disp,x_Win,&x_WinAttr,
417                  delay_Mode,screen_Pm);
418
419                 /* Restore the screen and wait */
420                 XCopyArea(x_Disp,screen_Pm,x_Win,x_Gc,0,0,x_WinAttr.width,
421                  x_WinAttr.height,0,0);
422                 delay_Time = time(NULL)+delay_Between;
423                 while (time(NULL) < delay_Time)
424                 {
425                         screenhack_handle_events(x_Disp);
426                         usleep(100000);
427                 }
428         }
429 }