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;
67 fprintf (stderr, "usage: %s [--wire] [--geom G | --root | --window-id ID]\n",
73 /* mostly lifted from xscreensaver/utils/visual.c... */
75 visual_depth (Display *dpy, int screen, Visual *visual)
77 XVisualInfo vi_in, *vi_out;
79 vi_in.screen = screen;
80 vi_in.visualid = XVisualIDFromVisual (visual);
81 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
83 if (! vi_out) abort ();
85 XFree ((char *) vi_out);
90 /* mostly lifted from xscreensaver/hacks/screenhack.c... */
91 static char *make_title_string (void)
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, ')');
102 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
104 return (strdup(version));
108 int init_view(int *argc, char *argv[])
113 Window on_window = 0;
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], "-window-id") &&
157 if (1 != sscanf (argv[ix+1], "%lu %c", &id, &c) &&
158 1 != sscanf (argv[ix+1], "0x%lx %c", &id, &c))
161 on_window = (Window) id;
163 else if (!strcmp(argv[ix], "-fullscreen") ||
164 !strcmp(argv[ix], "-full"))
166 if (on_root || geom) usage();
169 else if (!strcmp(argv[ix], "-wireframe") ||
170 !strcmp(argv[ix], "-wire"))
180 dpy = XOpenDisplay (dpystr);
183 screen = DefaultScreen (dpy);
185 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
186 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
188 if (on_root || on_window)
190 window = (on_window ? on_window : RootWindow (dpy, screen));
191 XGetWindowAttributes (dpy, window, &xgwa);
192 visual = xgwa.visual;
198 int ww = WidthOfScreen (DefaultScreenOfDisplay (dpy));
199 int hh = HeightOfScreen (DefaultScreenOfDisplay (dpy));
208 /* Since we're not linking against Xt or GLUT, we get to parse
209 the `-geometry' argument ourselves. YAY.
212 if (4 == sscanf (geom, "=%dx%d+%d+%d%c", &w, &h, &x, &y, &c))
214 else if (4 == sscanf (geom, "=%dx%d-%d+%d%c", &w, &h, &x, &y, &c))
216 else if (4 == sscanf (geom, "=%dx%d+%d-%d%c", &w, &h, &x, &y, &c))
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))
222 else if (2 == sscanf (geom, "+%d+%d%c", &x, &y, &c))
224 else if (2 == sscanf (geom, "-%d+%d%c", &x, &y, &c))
226 else if (2 == sscanf (geom, "+%d-%d%c", &x, &y, &c))
228 else if (2 == sscanf (geom, "-%d-%d%c", &x, &y, &c))
229 x = ww-w-x, y = hh-h-y;
232 fprintf (stderr, "%s: unparsable geometry: %s\n",
237 hints.flags = USSize;
240 if (x != undef && y != undef)
242 hints.flags |= USPosition;
248 /* Pick a good GL visual */
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
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 */
268 { GLX_RGBA, R, 1, G, 1, B, 1, D, 1, 0 } /* monochrome */
272 for (i = 0; i < sizeof(attrs)/sizeof(*attrs); i++)
274 XVisualInfo *vi = glXChooseVisual (dpy, screen, attrs[i]);
284 fprintf (stderr, "%s: unable to find a GL visual\n", progname);
290 if (x == undef) x = 0;
291 if (y == undef) y = 0;
293 xswa_mask = (CWEventMask | CWColormap |
294 CWBackPixel | CWBackingPixel | CWBorderPixel );
295 xswa.colormap = XCreateColormap (dpy, RootWindow (dpy, screen),
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);
302 window = XCreateWindow (dpy, RootWindow (dpy, screen),
304 visual_depth (dpy, screen, visual),
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,
322 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
324 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
326 XMapRaised (dpy, window);
331 /* Now hook up to GLX */
333 XVisualInfo vi_in, *vi_out;
335 vi_in.screen = screen;
336 vi_in.visualid = XVisualIDFromVisual (visual);
337 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
339 if (! vi_out) abort ();
341 glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
342 XFree((char *) vi_out);
346 fprintf(stderr, "%s: couldn't create GL context for root window.\n",
351 glXMakeCurrent (dpy, window, glx_context);
360 static void setup_window(void)
362 glEnable(GL_CULL_FACE);
363 glEnable(GL_LIGHTING);
365 glEnable(GL_DEPTH_TEST);
367 glEnable(GL_NORMALIZE);
370 /* callback: draw everything */
374 static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
375 static GLfloat gray[] = { 0.6, 0.6, 0.6, 1.0 };
377 glDrawBuffer(GL_BACK);
379 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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);
387 glShadeModel(GL_FLAT);
389 for (ix=0; ix<NUM_ELS; ix++) {
390 elem_t *el = &elist[ix];
392 glNormal3f(0.0, 0.0, 1.0);
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]);
403 if (wireframe) continue;
405 /* fill the square */
406 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, el->col);
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]);
418 glXSwapBuffers(dpy, window);
424 /* callback: new window size or exposure */
425 static void win_reshape(int width, int height)
427 GLfloat h = (GLfloat) height / (GLfloat) width;
429 glViewport(0, 0, (GLint) width, (GLint) height);
430 glMatrixMode(GL_PROJECTION);
432 glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
433 glMatrixMode(GL_MODELVIEW);
435 glTranslatef(0.0, 0.0, -40.0);
442 while (XPending (dpy))
446 XNextEvent (dpy, event);
447 switch (event->xany.type)
449 case ConfigureNotify:
451 XWindowAttributes xgwa;
452 XGetWindowAttributes (dpy, window, &xgwa);
453 win_reshape (xgwa.width, xgwa.height);
460 XLookupString (&event->xkey, &c, 1, &keysym, 0);
466 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
467 XBell (dpy, 0); /* beep for non-chord keys */
475 if (event->xclient.message_type != XA_WM_PROTOCOLS)
477 char *s = XGetAtomName(dpy, event->xclient.message_type);
478 if (!s) s = "(null)";
479 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
482 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
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",