/* * Copyright (C) 2002 Tugrul Galatali * * driver.c is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * driver.c is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "driver.h" #include "vroot.h" xstuff_t *XStuff; extern char *hack_name; /* * display parameters */ int rootWindow = False; int frameTime = 0; int be_nice = 0; int signalled = 0; void createWindow (int argc, char **argv) { XVisualInfo *visualInfo; GLXContext context; XStuff->screen_num = DefaultScreen (XStuff->display); XStuff->rootWindow = RootWindow (XStuff->display, XStuff->screen_num); if (rootWindow || XStuff->existingWindow) { XWindowAttributes gwa; Visual *visual; XVisualInfo templ; int outCount; XStuff->window = XStuff->existingWindow ? XStuff->existingWindow : XStuff->rootWindow; XGetWindowAttributes (XStuff->display, XStuff->window, &gwa); visual = gwa.visual; XStuff->windowWidth = gwa.width; XStuff->windowHeight = gwa.height; templ.screen = XStuff->screen_num; templ.visualid = XVisualIDFromVisual (visual); visualInfo = XGetVisualInfo (XStuff->display, VisualScreenMask | VisualIDMask, &templ, &outCount); if (!visualInfo) { fprintf (stderr, "%s: can't get GL visual for window 0x%lx.\n", XStuff->commandLineName, (unsigned long)XStuff->window); exit (1); } } else { int attributeList[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, 0 }; XSetWindowAttributes swa; XSizeHints hints; XWMHints wmHints; visualInfo = NULL; if (!(visualInfo = glXChooseVisual (XStuff->display, XStuff->screen_num, attributeList))) { fprintf (stderr, "%s: can't open GL visual.\n", XStuff->commandLineName); exit (1); } swa.colormap = XCreateColormap (XStuff->display, XStuff->rootWindow, visualInfo->visual, AllocNone); swa.border_pixel = swa.background_pixel = swa.backing_pixel = BlackPixel (XStuff->display, XStuff->screen_num); swa.event_mask = KeyPressMask | StructureNotifyMask; XStuff->windowWidth = DisplayWidth (XStuff->display, XStuff->screen_num) / 3; XStuff->windowHeight = DisplayHeight (XStuff->display, XStuff->screen_num) / 3; XStuff->window = XCreateWindow (XStuff->display, XStuff->rootWindow, 0, 0, XStuff->windowWidth, XStuff->windowHeight, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWBorderPixel | CWBackPixel | CWBackingPixel | CWColormap | CWEventMask, &swa); hints.flags = USSize; hints.width = XStuff->windowWidth; hints.height = XStuff->windowHeight; wmHints.flags = InputHint; wmHints.input = True; XmbSetWMProperties (XStuff->display, XStuff->window, hack_name, hack_name, argv, argc, &hints, &wmHints, NULL); } context = glXCreateContext (XStuff->display, visualInfo, 0, GL_TRUE); if (!context) { fprintf (stderr, "%s: can't open GLX context.\n", XStuff->commandLineName); exit (1); } if (!glXMakeCurrent (XStuff->display, XStuff->window, context)) { fprintf (stderr, "%s: can't set GL context.\n", XStuff->commandLineName); exit (1); } XFree (visualInfo); XMapWindow (XStuff->display, XStuff->window); } void clearBuffers() { int i; XEvent event; for (i = 0; i < 4; i++) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glXSwapBuffers (XStuff->display, XStuff->window); while (XPending (XStuff->display)) { XNextEvent (XStuff->display, &event); } } } void mainLoop (void) { int bFPS = False; XEvent event; Atom XA_WM_PROTOCOLS = XInternAtom (XStuff->display, "WM_PROTOCOLS", False); Atom XA_WM_DELETE_WINDOW = XInternAtom (XStuff->display, "WM_DELETE_WINDOW", False); struct timeval then, now, fps_time; int fps = 0; if (!rootWindow) { XSetWMProtocols (XStuff->display, XStuff->window, &XA_WM_DELETE_WINDOW, 1); } clearBuffers(); gettimeofday (&now, NULL); int frameTimeSoFar = 0; while (!signalled) { hack_draw (XStuff, (double)now.tv_sec + now.tv_usec / 1000000.0f, frameTimeSoFar / 1000000.0f); glXSwapBuffers (XStuff->display, XStuff->window); if (bFPS) { if (fps != -1) fps++; gettimeofday (&now, NULL); if (now.tv_sec > fps_time.tv_sec) { if (fps != -1) { printf ("%d fps\n", fps); } fps = 0; fps_time.tv_sec = now.tv_sec; fps_time.tv_usec = now.tv_usec; } } while (XPending (XStuff->display)) { KeySym keysym; char c = 0; XNextEvent (XStuff->display, &event); switch (event.type) { case ConfigureNotify: if ((int)XStuff->windowWidth != event.xconfigure.width || (int)XStuff->windowHeight != event.xconfigure.height) { XStuff->windowWidth = event.xconfigure.width; XStuff->windowHeight = event.xconfigure.height; clearBuffers(); hack_reshape (XStuff); } break; case KeyPress: XLookupString (&event.xkey, &c, 1, &keysym, 0); if (c == 'f') { bFPS = !bFPS; if (bFPS) { fps = -1; gettimeofday (&fps_time, NULL); } } if (c == 'q' || c == 'Q' || c == 3 || c == 27) return; break; case ClientMessage: if (event.xclient.message_type == XA_WM_PROTOCOLS) { if (event.xclient.data.l[0] == (int)XA_WM_DELETE_WINDOW) { return; } } break; case DestroyNotify: return; } } then = now; gettimeofday (&now, NULL); frameTimeSoFar = (now.tv_sec - then.tv_sec) * 1000000 + now.tv_usec - then.tv_usec; if (frameTime) { while (frameTimeSoFar < frameTime) { if (be_nice) { /* nanosleep on Linux/i386 seems completely ineffective for idling for < 20ms */ /* #ifdef HAVE_NANOSLEEP struct timespec hundreth; hundreth.tv_sec = 0; hundreth.tv_nsec = frameTime - frameTimeSoFar; nanosleep(&hundreth, NULL); #endif */ /* usleep(frameTime - frameTimeSoFar); */ struct timeval tv; tv.tv_sec = 0; tv.tv_usec = frameTime - frameTimeSoFar; select (0, 0, 0, 0, &tv); } gettimeofday (&now, NULL); frameTimeSoFar = (now.tv_sec - then.tv_sec) * 1000000 + now.tv_usec - then.tv_usec; } } else if (be_nice) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; select (0, 0, 0, 0, &tv); } } } void handle_global_opts (int c) { switch (c) { case 'r': rootWindow = 1; break; case 'x': c = strtol_minmaxdef (optarg, 10, 1, 10000, 1, 100, "--maxfps: "); frameTime = 1000000 / c; break; case 'n': be_nice = 1; break; } } int strtol_minmaxdef (char *optarg, int base, int min, int max, int type, int def, char *errmsg) { int result = strtol (optarg, (char **)NULL, base); if (result < min) { if (errmsg) { fprintf (stderr, errmsg); fprintf (stderr, "%d < %d, using %d instead.\n", result, min, type ? min : def); } return type ? min : def; } if (result > max) { if (errmsg) { fprintf (stderr, errmsg); fprintf (stderr, "%d > %d, using %d instead.\n", result, max, type ? max : def); } return type ? max : def; } return result; } void signalHandler (int sig) { signalled = 1; } int main (int argc, char *argv[]) { struct sigaction sa; char *display_name = NULL; /* Server to connect to */ int i, j; XStuff = (xstuff_t *) malloc (sizeof (xstuff_t)); XStuff->commandLineName = argv[0]; #ifdef BENCHMARK srandom (1); #else srandom ((unsigned)time (NULL)); #endif XStuff->existingWindow = 0; for (i = 0; i < argc; i++) { if (!strcmp (argv[i], "-window-id")) { if ((argv[i + 1][0] == '0') && ((argv[i + 1][1] == 'x') || (argv[i + 1][1] == 'X'))) { XStuff->existingWindow = strtol ((char *)(argv[i + 1] + 2), (char **)NULL, 16); } else { XStuff->existingWindow = strtol ((char *)(argv[i + 1]), (char **)NULL, 10); } for (j = i + 2; j < argc; j++) { argv[j - 2] = argv[j]; } argc -= 2; break; } } hack_handle_opts (argc, argv); XStuff->display = NULL; XStuff->window = 0; memset ((void *)&sa, 0, sizeof (struct sigaction)); sa.sa_handler = signalHandler; sigaction (SIGINT, &sa, 0); sigaction (SIGPIPE, &sa, 0); sigaction (SIGQUIT, &sa, 0); sigaction (SIGTERM, &sa, 0); /* * Connect to the X server. */ if (NULL == (XStuff->display = XOpenDisplay (display_name))) { fprintf (stderr, "%s: can't connect to X server %s\n", XStuff->commandLineName, XDisplayName (display_name)); exit (1); } createWindow (argc, argv); hack_init (XStuff); mainLoop (); /* * Clean up. */ if (XStuff->display) { if (XStuff->window) { hack_cleanup (XStuff); if (!((rootWindow) || (XStuff->existingWindow))) XDestroyWindow (XStuff->display, XStuff->window); } XCloseDisplay (XStuff->display); } return 0; }