1 /* StonerView: An eccentric visual toy.
2 Copyright 1998-2001 by Andrew Plotkin (erkyrath@eblong.com)
3 http://www.eblong.com/zarf/stonerview.html
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
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.
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
28 #include <X11/keysym.h>
32 #include "vroot.h" /* handle virtual root windows --- very important */
34 #include "version.h" /* get the xscreensaver package's version number */
35 #include "yarandom.h" /* use xscreensaver's RNG */
37 #include "stonerview-osc.h"
38 #include "stonerview-move.h"
40 static char *progclass = "StonerView";
41 static char *progname = 0;
43 #define FRAMERATE (50) /* milliseconds per frame */
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 */
49 static void setup_window(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);
59 static int wireframe = 0;
61 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
68 "usage: %s [--geom =WxH+X+Y | --fullscreen | --root] [--wire]\n",
74 /* mostly lifted from xscreensaver/utils/visual.c... */
76 visual_depth (Display *dpy, int screen, Visual *visual)
78 XVisualInfo vi_in, *vi_out;
80 vi_in.screen = screen;
81 vi_in.visualid = XVisualIDFromVisual (visual);
82 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
84 if (! vi_out) abort ();
86 XFree ((char *) vi_out);
91 /* mostly lifted from xscreensaver/hacks/screenhack.c... */
92 static char *make_title_string (void)
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, ')');
103 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
105 return (strdup(version));
109 int init_view(int *argc, char *argv[])
116 int x = undef, y = undef;
117 int w = 500, h = 500;
118 char *dpystr = (char *) getenv ("DISPLAY");
122 XWindowAttributes xgwa;
123 XSetWindowAttributes xswa;
124 unsigned long xswa_mask = 0;
126 GLXContext glx_context = 0;
130 memset (&hints, 0, sizeof(hints));
132 for (ix=1; ix<*argc; ix++)
134 if (argv[ix][0] == '-' && argv[ix][1] == '-')
136 if (!strcmp(argv[ix], "-geometry") ||
137 !strcmp(argv[ix], "-geom"))
139 if (on_root || fullscreen) usage();
142 else if (!strcmp(argv[ix], "-display") ||
143 !strcmp(argv[ix], "-disp") ||
144 !strcmp(argv[ix], "-dpy") ||
145 !strcmp(argv[ix], "-d"))
147 else if (!strcmp(argv[ix], "-root"))
149 if (geom || fullscreen) usage();
152 else if (!strcmp(argv[ix], "-fullscreen") ||
153 !strcmp(argv[ix], "-full"))
155 if (on_root || geom) usage();
158 else if (!strcmp(argv[ix], "-wireframe") ||
159 !strcmp(argv[ix], "-wire"))
169 dpy = XOpenDisplay (dpystr);
172 screen = DefaultScreen (dpy);
174 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
175 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
179 window = RootWindow (dpy, screen);
180 XGetWindowAttributes (dpy, window, &xgwa);
181 visual = xgwa.visual;
187 int ww = WidthOfScreen (DefaultScreenOfDisplay (dpy));
188 int hh = HeightOfScreen (DefaultScreenOfDisplay (dpy));
197 /* Since we're not linking against Xt or GLUT, we get to parse
198 the `-geometry' argument ourselves. YAY.
201 if (4 == sscanf (geom, "=%dx%d+%d+%d%c", &w, &h, &x, &y, &c))
203 else if (4 == sscanf (geom, "=%dx%d-%d+%d%c", &w, &h, &x, &y, &c))
205 else if (4 == sscanf (geom, "=%dx%d+%d-%d%c", &w, &h, &x, &y, &c))
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))
211 else if (2 == sscanf (geom, "+%d+%d%c", &x, &y, &c))
213 else if (2 == sscanf (geom, "-%d+%d%c", &x, &y, &c))
215 else if (2 == sscanf (geom, "+%d-%d%c", &x, &y, &c))
217 else if (2 == sscanf (geom, "-%d-%d%c", &x, &y, &c))
218 x = ww-w-x, y = hh-h-y;
221 fprintf (stderr, "%s: unparsable geometry: %s\n",
226 hints.flags = USSize;
229 if (x != undef && y != undef)
231 hints.flags |= USPosition;
237 /* Pick a good GL visual */
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
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 */
257 { GLX_RGBA, R, 1, G, 1, B, 1, D, 1, 0 } /* monochrome */
261 for (i = 0; i < sizeof(attrs)/sizeof(*attrs); i++)
263 XVisualInfo *vi = glXChooseVisual (dpy, screen, attrs[i]);
273 fprintf (stderr, "%s: unable to find a GL visual\n", progname);
279 if (x == undef) x = 0;
280 if (y == undef) y = 0;
282 xswa_mask = (CWEventMask | CWColormap |
283 CWBackPixel | CWBackingPixel | CWBorderPixel );
284 xswa.colormap = XCreateColormap (dpy, RootWindow (dpy, screen),
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);
291 window = XCreateWindow (dpy, RootWindow (dpy, screen),
293 visual_depth (dpy, screen, visual),
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,
311 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
313 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
315 XMapRaised (dpy, window);
320 /* Now hook up to GLX */
322 XVisualInfo vi_in, *vi_out;
324 vi_in.screen = screen;
325 vi_in.visualid = XVisualIDFromVisual (visual);
326 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
328 if (! vi_out) abort ();
330 glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
331 XFree((char *) vi_out);
335 fprintf(stderr, "%s: couldn't create GL context for root window.\n",
340 glXMakeCurrent (dpy, window, glx_context);
349 static void setup_window()
351 glEnable(GL_CULL_FACE);
352 glEnable(GL_LIGHTING);
354 glEnable(GL_DEPTH_TEST);
356 glEnable(GL_NORMALIZE);
359 /* callback: draw everything */
363 static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
364 static GLfloat gray[] = { 0.6, 0.6, 0.6, 1.0 };
366 glDrawBuffer(GL_BACK);
368 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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);
376 glShadeModel(GL_FLAT);
378 for (ix=0; ix<NUM_ELS; ix++) {
379 elem_t *el = &elist[ix];
381 glNormal3f(0.0, 0.0, 1.0);
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]);
392 if (wireframe) continue;
394 /* fill the square */
395 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, el->col);
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]);
407 glXSwapBuffers(dpy, window);
413 /* callback: new window size or exposure */
414 static void win_reshape(int width, int height)
416 GLfloat h = (GLfloat) height / (GLfloat) width;
418 glViewport(0, 0, (GLint) width, (GLint) height);
419 glMatrixMode(GL_PROJECTION);
421 glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
422 glMatrixMode(GL_MODELVIEW);
424 glTranslatef(0.0, 0.0, -40.0);
431 while (XPending (dpy))
435 XNextEvent (dpy, event);
436 switch (event->xany.type)
438 case ConfigureNotify:
440 XWindowAttributes xgwa;
441 XGetWindowAttributes (dpy, window, &xgwa);
442 win_reshape (xgwa.width, xgwa.height);
449 XLookupString (&event->xkey, &c, 1, &keysym, 0);
455 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
456 XBell (dpy, 0); /* beep for non-chord keys */
464 if (event->xclient.message_type != XA_WM_PROTOCOLS)
466 char *s = XGetAtomName(dpy, event->xclient.message_type);
467 if (!s) s = "(null)";
468 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
471 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
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",