http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.29.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,
68            "usage: %s [--geom =WxH+X+Y | --fullscreen | --root] [--wire]\n",
69            progname);
70   exit (1);
71 }
72
73
74 /* mostly lifted from xscreensaver/utils/visual.c... */
75 static int
76 visual_depth (Display *dpy, int screen, Visual *visual)
77 {
78   XVisualInfo vi_in, *vi_out;
79   int out_count, d;
80   vi_in.screen = screen;
81   vi_in.visualid = XVisualIDFromVisual (visual);
82   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
83                            &vi_in, &out_count);
84   if (! vi_out) abort ();
85   d = vi_out [0].depth;
86   XFree ((char *) vi_out);
87   return d;
88 }
89
90
91 /* mostly lifted from xscreensaver/hacks/screenhack.c... */
92 static char *make_title_string (void)
93 {
94   char version[255];
95   char *v = (char *) strdup(strchr(screensaver_id, ' '));
96   char *s1, *s2, *s3, *s4;
97   s1 = (char *) strchr(v,  ' '); s1++;
98   s2 = (char *) strchr(s1, ' ');
99   s3 = (char *) strchr(v,  '('); s3++;
100   s4 = (char *) strchr(s3, ')');
101   *s2 = 0;
102   *s4 = 0;
103   sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
104            progclass, s1, s3);
105   return (strdup(version));
106 }
107
108
109 int init_view(int *argc, char *argv[])
110 {
111   int ix;
112   int fullscreen = 0;
113   int on_root = 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], "-fullscreen") ||
153                !strcmp(argv[ix], "-full"))
154         {
155           if (on_root || geom) usage();
156           fullscreen = 1;
157         }
158       else if (!strcmp(argv[ix], "-wireframe") ||
159                !strcmp(argv[ix], "-wire"))
160         {
161           wireframe = 1;
162         }
163       else
164         {
165           usage();
166         }
167     }
168
169   dpy = XOpenDisplay (dpystr);
170   if (!dpy) exit (1);
171
172   screen = DefaultScreen (dpy);
173
174   XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
175   XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
176
177   if (on_root)
178     {
179       window = RootWindow (dpy, screen);
180       XGetWindowAttributes (dpy, window, &xgwa);
181       visual = xgwa.visual;
182       w = xgwa.width;
183       h = xgwa.height;
184     }
185   else
186     {
187       int ww = WidthOfScreen (DefaultScreenOfDisplay (dpy));
188       int hh = HeightOfScreen (DefaultScreenOfDisplay (dpy));
189
190       if (fullscreen)
191         {
192           w = ww;
193           h = hh;
194         }
195       else if (geom)
196         {
197           /* Since we're not linking against Xt or GLUT, we get to parse
198              the `-geometry' argument ourselves.  YAY.
199            */
200           char c;
201           if      (4 == sscanf (geom, "=%dx%d+%d+%d%c", &w, &h, &x, &y, &c))
202             ;
203           else if (4 == sscanf (geom, "=%dx%d-%d+%d%c", &w, &h, &x, &y, &c))
204             x = ww-w-x;
205           else if (4 == sscanf (geom, "=%dx%d+%d-%d%c", &w, &h, &x, &y, &c))
206             y = hh-h-y;
207           else if (4 == sscanf (geom, "=%dx%d-%d-%d%c", &w, &h, &x, &y, &c))
208             x = ww-w-x, y = hh-h-y;
209           else if (2 == sscanf (geom, "=%dx%d%c", &w, &h, &c))
210             ;
211           else if (2 == sscanf (geom, "+%d+%d%c", &x, &y, &c))
212             ;
213           else if (2 == sscanf (geom, "-%d+%d%c", &x, &y, &c))
214             x = ww-w-x;
215           else if (2 == sscanf (geom, "+%d-%d%c", &x, &y, &c))
216             y = hh-h-y;
217           else if (2 == sscanf (geom, "-%d-%d%c", &x, &y, &c))
218             x = ww-w-x, y = hh-h-y;
219           else
220             {
221               fprintf (stderr, "%s: unparsable geometry: %s\n",
222                        progname, geom);
223               exit (1);
224             }
225
226           hints.flags = USSize;
227           hints.width = w;
228           hints.height = h;
229           if (x != undef && y != undef)
230             {
231               hints.flags |= USPosition;
232               hints.x = x;
233               hints.y = y;
234             }
235         }
236
237       /* Pick a good GL visual */
238       {
239 # define R GLX_RED_SIZE
240 # define G GLX_GREEN_SIZE
241 # define B GLX_BLUE_SIZE
242 # define D GLX_DEPTH_SIZE
243 # define I GLX_BUFFER_SIZE
244 # define DB GLX_DOUBLEBUFFER
245
246         int attrs[][20] = {
247           { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, 0 }, /* rgb double */
248           { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, 0 },
249           { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, 0 },
250           { GLX_RGBA, R, 8, G, 8, B, 8, D, 8,     0 }, /* rgb single */
251           { GLX_RGBA, R, 4, G, 4, B, 4, D, 4,     0 },
252           { GLX_RGBA, R, 2, G, 2, B, 2, D, 2,     0 },
253           { I, 8,                       D, 8, DB, 0 }, /* cmap double */
254           { I, 4,                       D, 4, DB, 0 },
255           { I, 8,                       D, 8,     0 }, /* cmap single */
256           { I, 4,                       D, 4,     0 },
257           { GLX_RGBA, R, 1, G, 1, B, 1, D, 1,     0 }  /* monochrome */
258         };
259
260         int i;
261         for (i = 0; i < sizeof(attrs)/sizeof(*attrs); i++)
262           {
263             XVisualInfo *vi = glXChooseVisual (dpy, screen, attrs[i]);
264             if (vi)
265               {
266                 visual = vi->visual;
267                 XFree (vi);
268                 break;
269               }
270           }
271         if (!visual)
272           {
273             fprintf (stderr, "%s: unable to find a GL visual\n", progname);
274             exit (1);
275           }
276       }
277
278
279       if (x == undef) x = 0;
280       if (y == undef) y = 0;
281
282       xswa_mask = (CWEventMask | CWColormap |
283                    CWBackPixel | CWBackingPixel | CWBorderPixel );
284       xswa.colormap = XCreateColormap (dpy, RootWindow (dpy, screen),
285                                        visual, AllocNone);
286       xswa.background_pixel = BlackPixel (dpy, screen);
287       xswa.backing_pixel = xswa.background_pixel;
288       xswa.border_pixel = xswa.background_pixel;
289       xswa.event_mask = (KeyPressMask | ButtonPressMask | StructureNotifyMask);
290
291       window = XCreateWindow (dpy, RootWindow (dpy, screen),
292                               x, y, w, h, 0,
293                               visual_depth (dpy, screen, visual),
294                               InputOutput, visual,
295                               xswa_mask, &xswa);
296
297       {
298         XWMHints wmhints;
299
300         XTextProperty tp1, tp2;
301         char *v = make_title_string ();
302         XStringListToTextProperty (&v, 1, &tp1);
303         XStringListToTextProperty (&progclass, 1, &tp2);
304         wmhints.flags = InputHint;
305         wmhints.input = True;
306         XSetWMProperties (dpy, window, &tp1, &tp2, argv, *argc, &hints,
307                           &wmhints, 0);
308         free (v);
309       }
310
311       XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
312                        PropModeReplace,
313                        (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
314
315       XMapRaised (dpy, window);
316       XSync (dpy, False);
317     }
318
319
320   /* Now hook up to GLX */
321   {
322     XVisualInfo vi_in, *vi_out;
323     int out_count;
324     vi_in.screen = screen;
325     vi_in.visualid = XVisualIDFromVisual (visual);
326       vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
327                                &vi_in, &out_count);
328       if (! vi_out) abort ();
329
330       glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
331       XFree((char *) vi_out);
332
333       if (!glx_context)
334         {
335           fprintf(stderr, "%s: couldn't create GL context for root window.\n",
336                   progname);
337           exit(1);
338         }
339
340       glXMakeCurrent (dpy, window, glx_context);
341   }
342
343   setup_window();
344   win_reshape(w, h);
345
346   return 1;
347 }
348
349 static void setup_window()
350 {
351   glEnable(GL_CULL_FACE);
352   glEnable(GL_LIGHTING);
353   glEnable(GL_LIGHT0);
354   glEnable(GL_DEPTH_TEST);
355
356   glEnable(GL_NORMALIZE);
357 }
358
359 /* callback: draw everything */
360 void win_draw(void)
361 {
362   int ix;
363   static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
364   static GLfloat gray[] =  { 0.6, 0.6, 0.6, 1.0 };
365
366   glDrawBuffer(GL_BACK);
367
368   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
369
370   glPushMatrix();
371   glScalef(view_scale, view_scale, view_scale);
372   glRotatef(view_rotx, 1.0, 0.0, 0.0);
373   glRotatef(view_roty, 0.0, 1.0, 0.0);
374   glRotatef(view_rotz, 0.0, 0.0, 1.0);
375
376   glShadeModel(GL_FLAT);
377
378   for (ix=0; ix<NUM_ELS; ix++) {
379     elem_t *el = &elist[ix];
380
381     glNormal3f(0.0, 0.0, 1.0);
382
383     /* outline the square */
384     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (wireframe ? white : gray));
385     glBegin(GL_LINE_LOOP);
386     glVertex3f(el->pos[0]-el->vervec[0], el->pos[1]-el->vervec[1], el->pos[2]);
387     glVertex3f(el->pos[0]+el->vervec[1], el->pos[1]-el->vervec[0], el->pos[2]);
388     glVertex3f(el->pos[0]+el->vervec[0], el->pos[1]+el->vervec[1], el->pos[2]);
389     glVertex3f(el->pos[0]-el->vervec[1], el->pos[1]+el->vervec[0], el->pos[2]);
390     glEnd();
391
392     if (wireframe) continue;
393
394     /* fill the square */
395     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, el->col);
396     glBegin(GL_QUADS);
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
404   glPopMatrix();
405
406   glFinish();
407   glXSwapBuffers(dpy, window);
408
409   handle_events();
410 }
411
412
413 /* callback: new window size or exposure */
414 static void win_reshape(int width, int height)
415 {
416   GLfloat h = (GLfloat) height / (GLfloat) width;
417
418   glViewport(0, 0, (GLint) width, (GLint) height);
419   glMatrixMode(GL_PROJECTION);
420   glLoadIdentity();
421   glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
422   glMatrixMode(GL_MODELVIEW);
423   glLoadIdentity();
424   glTranslatef(0.0, 0.0, -40.0);
425 }
426
427
428 static void
429 handle_events (void)
430 {
431   while (XPending (dpy))
432     {
433       XEvent E;
434       XEvent *event = &E;
435       XNextEvent (dpy, event);
436       switch (event->xany.type)
437         {
438         case ConfigureNotify:
439           {
440             XWindowAttributes xgwa;
441             XGetWindowAttributes (dpy, window, &xgwa);
442             win_reshape (xgwa.width, xgwa.height);
443             break;
444           }
445         case KeyPress:
446           {
447             KeySym keysym;
448             char c = 0;
449             XLookupString (&event->xkey, &c, 1, &keysym, 0);
450             if (c == 'q' ||
451                 c == 'Q' ||
452                 c == 3 ||       /* ^C */
453                 c == 27)        /* ESC */
454               exit (0);
455             else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
456               XBell (dpy, 0);  /* beep for non-chord keys */
457           }
458           break;
459         case ButtonPress:
460           XBell (dpy, 0);
461           break;
462         case ClientMessage:
463           {
464             if (event->xclient.message_type != XA_WM_PROTOCOLS)
465               {
466                 char *s = XGetAtomName(dpy, event->xclient.message_type);
467                 if (!s) s = "(null)";
468                 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
469                          progname, s);
470               }
471             else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
472               {
473                 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
474                 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
475                 if (!s1) s1 = "(null)";
476                 if (!s2) s2 = "(null)";
477                 fprintf (stderr,"%s: unknown ClientMessage %s[%s] received!\n",
478                          progname, s1, s2);
479               }
480             else
481               {
482                 exit (0);
483               }
484           }
485           break;
486         }
487     }
488 }