http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.00.tar.gz
[xscreensaver] / hacks / glx / stonerview-view.c
1 /* StonerView: An eccentric visual toy.
2    Copyright 1998-2001 by Andrew Plotkin (erkyrath@eblong.com)
3    http://www.eblong.com/zarf/stonerview.html
4  
5    Permission to use, copy, modify, distribute, and sell this software and its
6    documentation for any purpose is hereby granted without fee, provided that
7    the above copyright notice appear in all copies and that both that
8    copyright notice and this permission notice appear in supporting
9    documentation.  No representations are made about the suitability of this
10    software for any purpose.  It is provided "as is" without express or 
11    implied warranty.
12 */
13
14 /* Ported away from GLUT (so that it can do `-root' and work with xscreensaver)
15    by Jamie Zawinski <jwz@jwz.org>, 22-Jan-2001.
16  */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <math.h>
24
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
28 #include <X11/keysym.h>
29 #include <GL/gl.h>
30 #include <GL/glx.h>
31
32 #include "vroot.h"      /* handle virtual root windows --- very important */
33
34 #include "version.h"    /* get the xscreensaver package's version number */
35 #include "yarandom.h"   /* use xscreensaver's RNG */
36
37 #include "stonerview-osc.h"
38 #include "stonerview-move.h"
39
40 static char *progclass = "StonerView";
41 static char *progname = 0;
42
43 #define FRAMERATE (50) /* milliseconds per frame */
44
45 static GLfloat view_rotx = -45.0, view_roty = 0.0, view_rotz = 0.0;
46 static GLfloat view_scale = 4.0;
47 /* static GLint prevtime = 0; / * for timing */
48
49 static void setup_window(void);
50
51 void win_draw(void);
52 static void win_reshape(int width, int height);
53 /* static void win_visible(int vis); */
54 /* static void win_idle(void); */
55 static void handle_events(void);
56
57 static Display *dpy;
58 static Window window;
59 static int wireframe = 0;
60
61 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
62
63
64 static void
65 usage (void)
66 {
67   fprintf (stderr, "usage: %s [--wire] [--geom G | --root | --window-id ID]\n",
68            progname);
69   exit (1);
70 }
71
72
73 /* mostly lifted from xscreensaver/utils/visual.c... */
74 static int
75 visual_depth (Display *dpy, int screen, Visual *visual)
76 {
77   XVisualInfo vi_in, *vi_out;
78   int out_count, d;
79   vi_in.screen = screen;
80   vi_in.visualid = XVisualIDFromVisual (visual);
81   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
82                            &vi_in, &out_count);
83   if (! vi_out) abort ();
84   d = vi_out [0].depth;
85   XFree ((char *) vi_out);
86   return d;
87 }
88
89
90 /* mostly lifted from xscreensaver/hacks/screenhack.c... */
91 static char *make_title_string (void)
92 {
93   char version[255];
94   char *v = (char *) strdup(strchr(screensaver_id, ' '));
95   char *s1, *s2, *s3, *s4;
96   s1 = (char *) strchr(v,  ' '); s1++;
97   s2 = (char *) strchr(s1, ' ');
98   s3 = (char *) strchr(v,  '('); s3++;
99   s4 = (char *) strchr(s3, ')');
100   *s2 = 0;
101   *s4 = 0;
102   sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
103            progclass, s1, s3);
104   return (strdup(version));
105 }
106
107
108 int init_view(int *argc, char *argv[])
109 {
110   int ix;
111   int fullscreen = 0;
112   int on_root = 0;
113   Window on_window = 0;
114
115   int undef = -65536;
116   int x = undef, y = undef;
117   int w = 500, h = 500;
118   char *dpystr = (char *) getenv ("DISPLAY");
119   char *geom = 0;
120   int screen;
121   Visual *visual;
122   XWindowAttributes xgwa;
123   XSetWindowAttributes xswa;
124   unsigned long xswa_mask = 0;
125   XSizeHints hints;
126   GLXContext glx_context = 0;
127
128   progname = argv[0];
129
130   memset (&hints, 0, sizeof(hints));
131
132   for (ix=1; ix<*argc; ix++)
133     {
134       if (argv[ix][0] == '-' && argv[ix][1] == '-')
135         argv[ix]++;
136       if (!strcmp(argv[ix], "-geometry") ||
137           !strcmp(argv[ix], "-geom"))
138         {
139           if (on_root || fullscreen) usage();
140           geom = argv[++ix];
141         }
142       else if (!strcmp(argv[ix], "-display") ||
143                !strcmp(argv[ix], "-disp") ||
144                !strcmp(argv[ix], "-dpy") ||
145                !strcmp(argv[ix], "-d"))
146         dpystr = argv[++ix];
147       else if (!strcmp(argv[ix], "-root"))
148         {
149           if (geom || fullscreen) usage();
150           on_root = 1;
151         }
152       else if (!strcmp(argv[ix], "-window-id") &&
153                *argc > ix+1)
154         {
155           unsigned long id;
156           char c;
157           if (1 != sscanf (argv[ix+1], "%lu %c", &id, &c) &&
158               1 != sscanf (argv[ix+1], "0x%lx %c", &id, &c))
159             usage();
160           ix++;
161           on_window = (Window) id;
162         }
163       else if (!strcmp(argv[ix], "-fullscreen") ||
164                !strcmp(argv[ix], "-full"))
165         {
166           if (on_root || geom) usage();
167           fullscreen = 1;
168         }
169       else if (!strcmp(argv[ix], "-wireframe") ||
170                !strcmp(argv[ix], "-wire"))
171         {
172           wireframe = 1;
173         }
174       else
175         {
176           usage();
177         }
178     }
179
180   dpy = XOpenDisplay (dpystr);
181   if (!dpy) exit (1);
182
183   screen = DefaultScreen (dpy);
184
185   XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
186   XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
187
188   if (on_root || on_window)
189     {
190       window = (on_window ? on_window : RootWindow (dpy, screen));
191       XGetWindowAttributes (dpy, window, &xgwa);
192       visual = xgwa.visual;
193       w = xgwa.width;
194       h = xgwa.height;
195     }
196   else
197     {
198       int ww = WidthOfScreen (DefaultScreenOfDisplay (dpy));
199       int hh = HeightOfScreen (DefaultScreenOfDisplay (dpy));
200
201       if (fullscreen)
202         {
203           w = ww;
204           h = hh;
205         }
206       else if (geom)
207         {
208           /* Since we're not linking against Xt or GLUT, we get to parse
209              the `-geometry' argument ourselves.  YAY.
210            */
211           char c;
212           if      (4 == sscanf (geom, "=%dx%d+%d+%d%c", &w, &h, &x, &y, &c))
213             ;
214           else if (4 == sscanf (geom, "=%dx%d-%d+%d%c", &w, &h, &x, &y, &c))
215             x = ww-w-x;
216           else if (4 == sscanf (geom, "=%dx%d+%d-%d%c", &w, &h, &x, &y, &c))
217             y = hh-h-y;
218           else if (4 == sscanf (geom, "=%dx%d-%d-%d%c", &w, &h, &x, &y, &c))
219             x = ww-w-x, y = hh-h-y;
220           else if (2 == sscanf (geom, "=%dx%d%c", &w, &h, &c))
221             ;
222           else if (2 == sscanf (geom, "+%d+%d%c", &x, &y, &c))
223             ;
224           else if (2 == sscanf (geom, "-%d+%d%c", &x, &y, &c))
225             x = ww-w-x;
226           else if (2 == sscanf (geom, "+%d-%d%c", &x, &y, &c))
227             y = hh-h-y;
228           else if (2 == sscanf (geom, "-%d-%d%c", &x, &y, &c))
229             x = ww-w-x, y = hh-h-y;
230           else
231             {
232               fprintf (stderr, "%s: unparsable geometry: %s\n",
233                        progname, geom);
234               exit (1);
235             }
236
237           hints.flags = USSize;
238           hints.width = w;
239           hints.height = h;
240           if (x != undef && y != undef)
241             {
242               hints.flags |= USPosition;
243               hints.x = x;
244               hints.y = y;
245             }
246         }
247
248       /* Pick a good GL visual */
249       {
250 # define R GLX_RED_SIZE
251 # define G GLX_GREEN_SIZE
252 # define B GLX_BLUE_SIZE
253 # define D GLX_DEPTH_SIZE
254 # define I GLX_BUFFER_SIZE
255 # define DB GLX_DOUBLEBUFFER
256
257         int attrs[][20] = {
258           { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, 0 }, /* rgb double */
259           { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, 0 },
260           { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, 0 },
261           { GLX_RGBA, R, 8, G, 8, B, 8, D, 8,     0 }, /* rgb single */
262           { GLX_RGBA, R, 4, G, 4, B, 4, D, 4,     0 },
263           { GLX_RGBA, R, 2, G, 2, B, 2, D, 2,     0 },
264           { I, 8,                       D, 8, DB, 0 }, /* cmap double */
265           { I, 4,                       D, 4, DB, 0 },
266           { I, 8,                       D, 8,     0 }, /* cmap single */
267           { I, 4,                       D, 4,     0 },
268           { GLX_RGBA, R, 1, G, 1, B, 1, D, 1,     0 }  /* monochrome */
269         };
270
271         int i;
272         for (i = 0; i < sizeof(attrs)/sizeof(*attrs); i++)
273           {
274             XVisualInfo *vi = glXChooseVisual (dpy, screen, attrs[i]);
275             if (vi)
276               {
277                 visual = vi->visual;
278                 XFree (vi);
279                 break;
280               }
281           }
282         if (!visual)
283           {
284             fprintf (stderr, "%s: unable to find a GL visual\n", progname);
285             exit (1);
286           }
287       }
288
289
290       if (x == undef) x = 0;
291       if (y == undef) y = 0;
292
293       xswa_mask = (CWEventMask | CWColormap |
294                    CWBackPixel | CWBackingPixel | CWBorderPixel );
295       xswa.colormap = XCreateColormap (dpy, RootWindow (dpy, screen),
296                                        visual, AllocNone);
297       xswa.background_pixel = BlackPixel (dpy, screen);
298       xswa.backing_pixel = xswa.background_pixel;
299       xswa.border_pixel = xswa.background_pixel;
300       xswa.event_mask = (KeyPressMask | ButtonPressMask | StructureNotifyMask);
301
302       window = XCreateWindow (dpy, RootWindow (dpy, screen),
303                               x, y, w, h, 0,
304                               visual_depth (dpy, screen, visual),
305                               InputOutput, visual,
306                               xswa_mask, &xswa);
307
308       {
309         XWMHints wmhints;
310
311         XTextProperty tp1, tp2;
312         char *v = make_title_string ();
313         XStringListToTextProperty (&v, 1, &tp1);
314         XStringListToTextProperty (&progclass, 1, &tp2);
315         wmhints.flags = InputHint;
316         wmhints.input = True;
317         XSetWMProperties (dpy, window, &tp1, &tp2, argv, *argc, &hints,
318                           &wmhints, 0);
319         free (v);
320       }
321
322       XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
323                        PropModeReplace,
324                        (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
325
326       XMapRaised (dpy, window);
327       XSync (dpy, False);
328     }
329
330
331   /* Now hook up to GLX */
332   {
333     XVisualInfo vi_in, *vi_out;
334     int out_count;
335     vi_in.screen = screen;
336     vi_in.visualid = XVisualIDFromVisual (visual);
337       vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
338                                &vi_in, &out_count);
339       if (! vi_out) abort ();
340
341       glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
342       XFree((char *) vi_out);
343
344       if (!glx_context)
345         {
346           fprintf(stderr, "%s: couldn't create GL context for root window.\n",
347                   progname);
348           exit(1);
349         }
350
351       glXMakeCurrent (dpy, window, glx_context);
352   }
353
354   setup_window();
355   win_reshape(w, h);
356
357   return 1;
358 }
359
360 static void setup_window(void)
361 {
362   glEnable(GL_CULL_FACE);
363   glEnable(GL_LIGHTING);
364   glEnable(GL_LIGHT0);
365   glEnable(GL_DEPTH_TEST);
366
367   glEnable(GL_NORMALIZE);
368 }
369
370 /* callback: draw everything */
371 void win_draw(void)
372 {
373   int ix;
374   static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
375   static GLfloat gray[] =  { 0.6, 0.6, 0.6, 1.0 };
376
377   glDrawBuffer(GL_BACK);
378
379   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
380
381   glPushMatrix();
382   glScalef(view_scale, view_scale, view_scale);
383   glRotatef(view_rotx, 1.0, 0.0, 0.0);
384   glRotatef(view_roty, 0.0, 1.0, 0.0);
385   glRotatef(view_rotz, 0.0, 0.0, 1.0);
386
387   glShadeModel(GL_FLAT);
388
389   for (ix=0; ix<NUM_ELS; ix++) {
390     elem_t *el = &elist[ix];
391
392     glNormal3f(0.0, 0.0, 1.0);
393
394     /* outline the square */
395     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (wireframe ? white : gray));
396     glBegin(GL_LINE_LOOP);
397     glVertex3f(el->pos[0]-el->vervec[0], el->pos[1]-el->vervec[1], el->pos[2]);
398     glVertex3f(el->pos[0]+el->vervec[1], el->pos[1]-el->vervec[0], el->pos[2]);
399     glVertex3f(el->pos[0]+el->vervec[0], el->pos[1]+el->vervec[1], el->pos[2]);
400     glVertex3f(el->pos[0]-el->vervec[1], el->pos[1]+el->vervec[0], el->pos[2]);
401     glEnd();
402
403     if (wireframe) continue;
404
405     /* fill the square */
406     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, el->col);
407     glBegin(GL_QUADS);
408     glVertex3f(el->pos[0]-el->vervec[0], el->pos[1]-el->vervec[1], el->pos[2]);
409     glVertex3f(el->pos[0]+el->vervec[1], el->pos[1]-el->vervec[0], el->pos[2]);
410     glVertex3f(el->pos[0]+el->vervec[0], el->pos[1]+el->vervec[1], el->pos[2]);
411     glVertex3f(el->pos[0]-el->vervec[1], el->pos[1]+el->vervec[0], el->pos[2]);
412     glEnd();
413   }
414
415   glPopMatrix();
416
417   glFinish();
418   glXSwapBuffers(dpy, window);
419
420   handle_events();
421 }
422
423
424 /* callback: new window size or exposure */
425 static void win_reshape(int width, int height)
426 {
427   GLfloat h = (GLfloat) height / (GLfloat) width;
428
429   glViewport(0, 0, (GLint) width, (GLint) height);
430   glMatrixMode(GL_PROJECTION);
431   glLoadIdentity();
432   glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
433   glMatrixMode(GL_MODELVIEW);
434   glLoadIdentity();
435   glTranslatef(0.0, 0.0, -40.0);
436 }
437
438
439 static void
440 handle_events (void)
441 {
442   while (XPending (dpy))
443     {
444       XEvent E;
445       XEvent *event = &E;
446       XNextEvent (dpy, event);
447       switch (event->xany.type)
448         {
449         case ConfigureNotify:
450           {
451             XWindowAttributes xgwa;
452             XGetWindowAttributes (dpy, window, &xgwa);
453             win_reshape (xgwa.width, xgwa.height);
454             break;
455           }
456         case KeyPress:
457           {
458             KeySym keysym;
459             char c = 0;
460             XLookupString (&event->xkey, &c, 1, &keysym, 0);
461             if (c == 'q' ||
462                 c == 'Q' ||
463                 c == 3 ||       /* ^C */
464                 c == 27)        /* ESC */
465               exit (0);
466             else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
467               XBell (dpy, 0);  /* beep for non-chord keys */
468           }
469           break;
470         case ButtonPress:
471           XBell (dpy, 0);
472           break;
473         case ClientMessage:
474           {
475             if (event->xclient.message_type != XA_WM_PROTOCOLS)
476               {
477                 char *s = XGetAtomName(dpy, event->xclient.message_type);
478                 if (!s) s = "(null)";
479                 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
480                          progname, s);
481               }
482             else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
483               {
484                 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
485                 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
486                 if (!s1) s1 = "(null)";
487                 if (!s2) s2 = "(null)";
488                 fprintf (stderr,"%s: unknown ClientMessage %s[%s] received!\n",
489                          progname, s1, s2);
490               }
491             else
492               {
493                 exit (0);
494               }
495           }
496           break;
497         }
498     }
499 }