1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* deluxe ---  */
3 
4 #if 0
5 static const char sccsid[] = "@(#)deluxe.c	5.22 2006/03/07 xlockmore";
6 #endif
7 
8 /* xscreensaver, Copyright (c) 1999, 2001, 2002 Jamie Zawinski <jwz AT jwz.org>
9  *
10  * Permission to use, copy, modify, distribute, and sell this software and its
11  * documentation for any purpose is hereby granted without fee, provided that
12  * the above copyright notice appear in all copies and that both that
13  * copyright notice and this permission notice appear in supporting
14  * documentation.  No representations are made about the suitability of this
15  * software for any purpose.  It is provided "as is" without express or
16  * implied warranty.
17  *
18  * adapted for xlockmore : March 2006
19  */
20 
21 #ifdef STANDALONE
22 # define MODE_deluxe
23 
24 # define DEFAULTS	"*delay: 5000 \n" \
25 			"*count: 5 \n" \
26 			"*ncolors: 64 \n" \
27 			"*fullrandom: True \n" \
28 			"*verbose: False \n" \
29 
30 #if 0
31 # define DEFAULTS	"*delay: 10000 \n" \
32 			"*count: 5 \n", \
33 			"*ncolors: 20 \n", \
34 			"*nlayers: 0 \n", \
35 			"*doubleBuffer: True\n"
36 			".background: black\n", \
37 			"foreground: white\n", \
38 
39 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
40 			, "*useDBE: True\n", \
41 
42 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
43 #endif
44 # define free_deluxe 0
45 # define reshape_deluxe 0
46 # define deluxe_handle_event 0
47 # include "xlockmore.h"		/* in xscreensaver distribution */
48 extern Bool allocate_alpha_colors (Screen *screen, Visual *visual,
49                                    Colormap cmap,
50                                    int *nplanesP, Bool additive_p,
51                                    unsigned long **plane_masks,
52                                    unsigned long *base_pixelP ,
53                                    ModeInfo* mi );
54 #else /* STANDALONE */
55 # include "xlock.h"		/* in xlockmore distribution */
56 # include "color.h"
57 #endif /* STANDALONE */
58 
59 #ifdef MODE_deluxe
60 
61 #include <math.h>
62 
63 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
64 # include "xdbe.h"
65 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
66 
67 #ifndef countof
68 #define countof(x) (sizeof(x)/sizeof(*(x)))
69 #endif
70 
71 #define DEF_THICKNESS "50"
72 #define DEF_SPEED "15"
73 #define DEF_TRANSPARENT "False"
74 #ifdef WIN32
75 #define DEF_DB "False"
76 #else
77 #define DEF_DB "True"
78 #endif
79 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
80 #define DEF_USEDBECLEAR "True"
81 #endif
82 #define DEF_MONO "False"
83 #define DEF_PLANES "0"
84 
85 static int st_thickness;
86 static int st_speed;
87 static Bool st_transparent;
88 static Bool st_db;
89 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
90 static Bool st_dbeclear_p;
91 #endif
92 static Bool st_mono;
93 static int st_planes;
94 
95 struct throbber {
96   int x, y;
97   int size;
98   int max_size;
99   int thickness;
100   int speed;
101   int fuse;
102   GC gc;
103   void (*draw) (Display *, Drawable, struct throbber *);
104 };
105 
106 typedef struct {
107   Bool transparent_p;
108   int nplanes;
109   unsigned long base_pixel, *plane_masks;
110    int count;
111    int ncolors;
112    Bool dbuf;
113 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
114    Bool dbeclear_p;
115    XdbeBackBuffer backb;
116 #endif
117    XColor *colors;
118    GC erase_gc;
119    struct throbber **throbbers;
120    XWindowAttributes xgwa;
121    Pixmap b, ba, bb;	/* double-buffer to reduce flicker */
122 } deluxestruct;
123 
124 static deluxestruct *deluxes = (deluxestruct *) NULL;
125 
126 static XrmOptionDescRec opts [] = {
127   { "-thickness",	"deluxe.thickness",	XrmoptionSepArg,  (caddr_t) NULL },
128   { "-speed",		"deluxe.speed",	XrmoptionSepArg,  (caddr_t) NULL },
129   { "-planes",		"deluxe.planes",	XrmoptionSepArg,  (caddr_t) NULL },
130   { "-transparent",	"deluxe.transparent",	 XrmoptionNoArg,  (caddr_t) "on" },
131   { "+transparent",	"deluxe.transparent",	 XrmoptionNoArg,  (caddr_t) "off" },
132 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
133   { "-useDBEClear",		"deluxe.useDBEClear", XrmoptionNoArg,  (caddr_t) "on" },
134   { "+useDBEClear",		"deluxe.useDBEClear", XrmoptionNoArg,  (caddr_t) "off" },
135 #endif
136   { "-mono",		"deluxe.mono", XrmoptionNoArg,  (caddr_t) "on" },
137   { "+mono",		"deluxe.mono", XrmoptionNoArg,  (caddr_t) "off" },
138   { "-db",		"deluxe.doubleBuffer", XrmoptionNoArg,  (caddr_t) "on" },
139   { "+db",		"deluxe.doubleBuffer", XrmoptionNoArg,  (caddr_t) "off" }
140 };
141 
142 static argtype vars[] =
143 {
144 	{(void *) & st_thickness, (char *) "thickness", (char *) "thickness",
145 	     (char *) DEF_THICKNESS, t_Int},
146 	{(void *) & st_speed, (char *) "speed", (char *) "speed",
147 	     (char *) DEF_SPEED, t_Int},
148 	{(void *) & st_planes, (char *) "planes", (char *) "Planes",
149 	     (char *) DEF_PLANES, t_Int},
150 	{(void *) & st_transparent, (char *) "transparent",
151 	     (char *) "Transparent", (char *) DEF_TRANSPARENT, t_Bool},
152 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
153 	{(void *) & st_dbeclear_p, (char *) "useDBEClear",
154 	     (char *) "useDBEClear",
155 	     (char *) DEF_USEDBECLEAR, t_Bool},
156 #endif
157 	{(void *) & st_mono, (char *) "mono", (char *) "mono",
158 	     (char *) DEF_MONO, t_Bool},
159 	{(void *) & st_db, (char *) "db", (char *) "db",
160 	     (char *) DEF_DB, t_Bool}
161 
162 };
163 static OptionStruct desc[] =
164 {
165 	{(char *) "-thickness num", (char *) "Figure thickness"},
166 	{(char *) "-speed num", (char *) "Animation speed"},
167 	{(char *) "-planes num", (char *) "Number of planes"},
168 	{(char *) "-/+transparent", (char *) "turn on/off transparency"},
169 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
170 	{(char *) "-/+useDBEClear", (char *) "turn on/off useDBEClear"},
171 #endif
172 	{(char *) "-/+mono", (char *) "turn on/off monochromatic mode"},
173 	{(char *) "-/+db", (char *) "turn on/off double buffering"}
174 };
175 
176 ENTRYPOINT ModeSpecOpt deluxe_opts =
177 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
178 
179 #ifdef USE_MODULES
180 ModStruct   deluxe_description =
181 {"deluxe", "init_deluxe", "draw_deluxe", "release_deluxe",
182  "(char *) NULL", "init_deluxe", (char *) NULL, &deluxe_opts,
183  5000, 5, 1, 1, 64, 1.0, "",
184  "Shows pulsing sequence of stars, circles, and lines.", 0, NULL};
185 
186 #endif
187 
188 
189 static void
free_deluxe_screen(Display * dpy,deluxestruct * dlp)190 free_deluxe_screen(Display *dpy, deluxestruct *dlp)
191 {
192    int i;
193 
194    if (dlp == NULL) {
195      return;
196    }
197    for (i = 0; i < dlp->count; i++)
198      if ( dlp->throbbers[i] )
199        {
200           if (dlp->throbbers[i]->gc != None)
201              XFreeGC (dpy, dlp->throbbers[i]->gc);
202 	  free( dlp->throbbers[i] );
203        }
204    if ( dlp->throbbers )
205      {
206 	free( dlp->throbbers );
207 	dlp->throbbers = ( struct throbber** ) NULL;
208      }
209    if ( dlp->colors )
210      {
211 	free( dlp->colors );
212 	dlp->colors = (XColor *) NULL;
213      }
214    if ( dlp->plane_masks )
215      {
216 	free( dlp->plane_masks );
217 	dlp->plane_masks = (unsigned long*) NULL;
218      }
219    if (dlp->erase_gc != None)
220      {
221         XFreeGC (dpy, dlp->erase_gc);
222    	dlp->erase_gc = None;
223      }
224    dlp = NULL;
225 }
226 
227 /* http://mathworld.wolfram.com/Pentagram.html
228  * R / p
229  * R=sqrt((25-11*sqrt(5))/10)
230  * p=sqrt((5-sqrt(5))/10)
231  * sqrt(14-6*sqrt(5))/2
232  */
233 static void
deluxe_draw_star(Display * dpy,Drawable w,struct throbber * t)234 deluxe_draw_star (Display *dpy, Drawable w, struct throbber *t)
235 {
236   XPoint points[11];
237   int x = t->x;
238   int y = t->y;
239   int s = (int) (t->size / 0.381966);
240   int s2 = t->size;
241   double c = M_PI * 2.0;
242   double o = -M_PI / 2.0;
243 
244   points[0].x = (short) (x + s  * cos(o + 0.0*c));
245   points[0].y = (short) (y + s  * sin(o + 0.0*c));
246   points[1].x = (short) (x + s2 * cos(o + 0.1*c));
247   points[1].y = (short) (y + s2 * sin(o + 0.1*c));
248   points[2].x = (short) (x + s  * cos(o + 0.2*c));
249   points[2].y = (short) (y + s  * sin(o + 0.2*c));
250   points[3].x = (short) (x + s2 * cos(o + 0.3*c));
251   points[3].y = (short) (y + s2 * sin(o + 0.3*c));
252   points[4].x = (short) (x + s  * cos(o + 0.4*c));
253   points[4].y = (short) (y + s  * sin(o + 0.4*c));
254   points[5].x = (short) (x + s2 * cos(o + 0.5*c));
255   points[5].y = (short) (y + s2 * sin(o + 0.5*c));
256   points[6].x = (short) (x + s  * cos(o + 0.6*c));
257   points[6].y = (short) (y + s  * sin(o + 0.6*c));
258   points[7].x = (short) (x + s2 * cos(o + 0.7*c));
259   points[7].y = (short) (y + s2 * sin(o + 0.7*c));
260   points[8].x = (short) (x + s  * cos(o + 0.8*c));
261   points[8].y = (short) (y + s  * sin(o + 0.8*c));
262   points[9].x = (short) (x + s2 * cos(o + 0.9*c));
263   points[9].y = (short) (y + s2 * sin(o + 0.9*c));
264   points[10] = points[0];
265 
266   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
267 }
268 
269 static void
draw_circle(Display * dpy,Drawable w,struct throbber * t)270 draw_circle (Display *dpy, Drawable w, struct throbber *t)
271 {
272   XDrawArc (dpy, w, t->gc,
273             t->x - t->size / 2,
274             t->y - t->size / 2,
275             t->size, t->size,
276             0, 360*64);
277 }
278 
279 static void
draw_hlines(Display * dpy,Drawable w,struct throbber * t)280 draw_hlines (Display *dpy, Drawable w, struct throbber *t)
281 {
282   XDrawLine (dpy, w, t->gc, 0,
283              t->y - t->size, t->max_size,
284              t->y - t->size);
285   XDrawLine (dpy, w, t->gc, 0,
286              t->y + t->size, t->max_size,
287              t->y + t->size);
288 }
289 
290 static void
draw_vlines(Display * dpy,Drawable w,struct throbber * t)291 draw_vlines (Display *dpy, Drawable w, struct throbber *t)
292 {
293   XDrawLine (dpy, w, t->gc,
294              t->x - t->size, 0,
295              t->x - t->size, t->max_size);
296   XDrawLine (dpy, w, t->gc,
297              t->x + t->size, 0,
298              t->x + t->size, t->max_size);
299 }
300 
301 static void
draw_corners(Display * dpy,Drawable w,struct throbber * t)302 draw_corners (Display *dpy, Drawable w, struct throbber *t)
303 {
304   int s = (t->size + t->thickness) / 2;
305   XPoint points[3];
306 
307   points[0].x = 0;        points[0].y = t->y - s;
308   points[1].x = t->x - s; points[1].y = t->y - s;
309   points[2].x = t->x - s; points[2].y = 0;
310   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
311 
312   points[0].x = 0;        points[0].y = t->y + s;
313   points[1].x = t->x - s; points[1].y = t->y + s;
314   points[2].x = t->x - s; points[2].y = t->max_size;
315   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
316 
317   points[0].x = t->x + s;    points[0].y = 0;
318   points[1].x = t->x + s;    points[1].y = t->y - s;
319   points[2].x = t->max_size; points[2].y = t->y - s;
320   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
321 
322   points[0].x = t->x + s;    points[0].y = t->max_size;
323   points[1].x = t->x + s;    points[1].y = t->y + s;
324   points[2].x = t->max_size; points[2].y = t->y + s;
325   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
326 }
327 
328 
329 static struct throbber *
make_throbber(Display * dpy,Drawable d,int w,int h,unsigned long pixel,deluxestruct * dlp,ModeInfo * mi)330 make_throbber (Display *dpy, Drawable d, int w, int h, unsigned long pixel ,
331 	       deluxestruct *dlp, ModeInfo * mi)
332 {
333   XGCValues gcv;
334   unsigned long flags;
335   struct throbber *t = (struct throbber *) malloc (sizeof (*t));
336   t->x = w / 2;
337   t->y = h / 2;
338   t->max_size = w;
339   t->speed = st_speed;
340   t->fuse = 1 + (NRAND(4));
341   t->thickness = st_thickness;
342 
343   if (t->speed < 0) t->speed = -t->speed;
344   t->speed += (((NRAND(t->speed)) / 2) - (t->speed / 2));
345   if (t->speed > 0) t->speed = -t->speed;
346 
347   if (NRAND(4))
348     t->size = t->max_size;
349   else
350     t->size = t->thickness, t->speed = -t->speed;
351 
352   flags = GCForeground;
353   if (dlp->transparent_p)
354     {
355       gcv.foreground = (unsigned long) ~0L;
356       gcv.plane_mask = dlp->base_pixel |
357 	      dlp->plane_masks[NRAND(dlp->nplanes)];
358       flags |= GCPlaneMask;
359     }
360   else
361     {
362 #ifdef WIN32
363       gcv.foreground = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
364 #else
365       gcv.foreground = pixel;
366 #endif
367     }
368 
369   gcv.line_width = t->thickness;
370   gcv.line_style = LineSolid;
371   gcv.cap_style = CapProjecting;
372   gcv.join_style = JoinMiter;
373 
374   flags |= (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
375   t->gc = XCreateGC (dpy, d, flags, &gcv);
376   switch (NRAND(11)) {
377   case 0: case 1: case 2: case 3: t->draw = deluxe_draw_star; break;
378   case 4: case 5: case 6: case 7: t->draw = draw_circle; break;
379   case 8: t->draw = draw_hlines; break;
380   case 9: t->draw = draw_vlines; break;
381   case 10: t->draw = draw_corners; break;
382   default: abort(); break;
383   }
384 
385   return t;
386 }
387 
388 static int
throb(Display * dpy,Drawable window,struct throbber * t)389 throb (Display *dpy, Drawable window, struct throbber *t)
390 {
391   t->size += t->speed;
392   if (t->size <= (t->thickness / 2))
393     {
394       t->speed = -t->speed;
395       t->size += (t->speed * 2);
396     }
397   else if (t->size > t->max_size)
398     {
399       t->speed = -t->speed;
400       t->size += (t->speed * 2);
401       t->fuse--;
402     }
403 
404   if (t->fuse <= 0)
405     {
406       XFreeGC (dpy, t->gc);
407       memset (t, 0, sizeof(*t));
408       free (t);
409       return -1;
410     }
411   else
412     {
413       t->draw (dpy, window, t);
414       return 0;
415     }
416 }
417 
418 ENTRYPOINT void
init_deluxe(ModeInfo * mi)419 init_deluxe(ModeInfo * mi)
420 {
421   deluxestruct *dlp;
422   Display *dpy = MI_DISPLAY(mi);
423   Window window = MI_WINDOW(mi);
424   XGCValues gcv;
425   Bool writeable = (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2);
426   int i;
427 
428    /* initialize */
429    MI_INIT(mi, deluxes);
430    dlp = &deluxes[MI_SCREEN(mi)];
431 
432   dlp->count = MI_COUNT(mi);
433   dlp->ncolors =  MI_NCOLORS(mi);
434   dlp->dbuf = st_db;
435 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
436   dlp->dbeclear_p = st_dbeclear_p;
437 #endif
438   dlp->colors = 0;
439   dlp->erase_gc = 0;
440   dlp->b=0;
441   dlp->ba=0;
442   dlp->bb=0;	/* double-buffer to reduce flicker */
443 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
444   dlp->backb =0;
445 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
446 
447   XGetWindowAttributes (dpy, window, &dlp->xgwa);
448 
449   dlp->transparent_p = st_transparent;
450 
451   dlp->colors = (XColor *) calloc (sizeof(XColor), dlp->ncolors);
452 
453   if ( st_mono )
454     {
455     MONO:
456       dlp->ncolors = 1;
457       dlp->colors[0].pixel = MI_WHITE_PIXEL(mi);
458     }
459   else if (dlp->transparent_p)
460     {
461       dlp->nplanes = st_planes;
462       if (dlp->nplanes <= 0)
463         dlp->nplanes = NRAND(dlp->xgwa.depth-2) + 2;
464 
465       allocate_alpha_colors (dlp->xgwa.screen, dlp->xgwa.visual,
466 		dlp->xgwa.colormap,
467                 &dlp->nplanes, True, &dlp->plane_masks,
468 		&dlp->base_pixel , mi );
469       if (dlp->nplanes <= 1)
470 	{
471 	  fprintf (stderr,
472          "deluxe: couldn't allocate any color planes; turning transparency off.\n"
473 		  );
474           dlp->transparent_p = False;
475 	  goto COLOR;
476 	}
477     }
478   else
479     {
480     COLOR:
481       make_random_colormap (
482 #ifdef STANDALONE
483 		dlp->xgwa.screen, dlp->xgwa.visual,
484 		dlp->xgwa.colormap, dlp->colors, &dlp->ncolors,
485 		True, True, &writeable, True
486 #else
487 		mi,
488 		dlp->xgwa.colormap, dlp->colors, &dlp->ncolors,
489 		True, True, &writeable
490 #endif
491 		);
492       if (dlp->ncolors < 2)
493         goto MONO;
494     }
495 
496   if (dlp->dbuf)
497     {
498 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
499       if (dlp->dbeclear_p)
500         dlp->b = xdbe_get_backbuffer (dpy, window, XdbeBackground);
501       else
502         dlp->b = xdbe_get_backbuffer (dpy, window, XdbeUndefined);
503       dlp->backb = dlp->b;
504 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
505 
506       if (!dlp->b)
507         {
508           dlp->ba = XCreatePixmap (dpy, window, dlp->xgwa.width,
509 				   dlp->xgwa.height, dlp->xgwa.depth);
510           dlp->bb = XCreatePixmap (dpy, window, dlp->xgwa.width,
511 				   dlp->xgwa.height, dlp->xgwa.depth);
512           dlp->b = dlp->ba;
513         }
514     }
515   else
516     {
517       dlp->b = window;
518     }
519 
520   dlp->throbbers = (struct throbber **) calloc ( dlp->count, sizeof(struct throbber *));
521   for (i = 0; i < dlp->count; i++)
522     dlp->throbbers[i] = make_throbber (dpy, dlp->b, dlp->xgwa.width,
523 				       dlp->xgwa.height,
524                                   dlp->colors[ NRAND( dlp->ncolors )].pixel ,
525 				       dlp, mi);
526 
527   gcv.foreground = MI_BLACK_PIXEL(mi);
528   dlp->erase_gc = XCreateGC (dpy, dlp->b, GCForeground, &gcv);
529 
530   if ( dlp->ba) XFillRectangle (dpy, dlp->ba, dlp->erase_gc, 0, 0,
531 				dlp->xgwa.width, dlp->xgwa.height);
532   if ( dlp->bb) XFillRectangle (dpy, dlp->bb, dlp->erase_gc, 0, 0,
533 				dlp->xgwa.width, dlp->xgwa.height);
534 }
535 
536 ENTRYPOINT void
draw_deluxe(ModeInfo * mi)537 draw_deluxe(ModeInfo * mi)
538 {
539    deluxestruct *dlp = &deluxes[MI_SCREEN(mi)];
540    Display *dpy = MI_DISPLAY(mi);
541    Window window = MI_WINDOW(mi);
542    int i;
543 
544    if (deluxes == NULL)
545 	return;
546    MI_IS_DRAWN(mi) = True;
547 
548 
549 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
550       if (!dlp->dbeclear_p || !dlp->backb )
551 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
552         XFillRectangle (dpy, dlp->b, dlp->erase_gc, 0, 0, dlp->xgwa.width,
553 			dlp->xgwa.height);
554 
555       for (i = 0; i < dlp->count; i++)
556         if (throb (dpy, dlp->b, dlp->throbbers[i]) < 0)
557           dlp->throbbers[i] = make_throbber (dpy, dlp->b, dlp->xgwa.width,
558 					     dlp->xgwa.height,
559                                         dlp->colors[ NRAND( dlp->ncolors )].pixel ,
560 					     dlp, mi);
561 
562 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
563       if (dlp->backb)
564         {
565           XdbeSwapInfo info[1];
566           info[0].swap_window = window;
567           info[0].swap_action = (dlp->dbeclear_p ? XdbeBackground : XdbeUndefined);
568           XdbeSwapBuffers (dpy, info, 1);
569         }
570       else
571 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
572       if (dlp->dbuf)
573         {
574           XCopyArea (dpy, dlp->b, window, dlp->erase_gc, 0, 0,
575                      dlp->xgwa.width, dlp->xgwa.height, 0, 0);
576           dlp->b = (dlp->b == dlp->ba ? dlp->bb : dlp->ba);
577         }
578       XSync (dpy, False);
579 }
580 
581 ENTRYPOINT void
release_deluxe(ModeInfo * mi)582 release_deluxe(ModeInfo * mi)
583 {
584    if (deluxes != NULL) {
585       int screen;
586 
587       for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
588 	free_deluxe_screen(MI_DISPLAY(mi), &deluxes[screen]);
589       free(deluxes);
590       deluxes = (deluxestruct *) NULL;
591    }
592 }
593 
594 XSCREENSAVER_MODULE ("Deluxe", deluxe)
595 
596 #endif /* MODE_deluxe */
597