/* * zoom.cpp * * Copyright (C) 2009 Matthias Schiffer * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include "Game.h" #include "MathUtil.h" #include "config.h" #include "gl.h" #ifdef _WIN32 #else #include #include #include #include #include #include #include #endif #ifdef _WIN32 bool WGLInit(HINSTANCE hInstance, HWND *hWnd, HDC *hDC, HGLRC *hRC); void WGLUninit(HWND hWnd, HDC hDC, HGLRC hRC); LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); bool WGLInit(HINSTANCE hInstance, HWND *hWnd, HDC *hDC, HGLRC *hRC) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC)wndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = 0; wc.lpszMenuName = 0; wc.lpszClassName = "Zoom"; if (!RegisterClass(&wc)) { MessageBox(0, "Failed To Register The Window Class.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; DWORD dwStyle = WS_OVERLAPPEDWINDOW; RECT windowRect; windowRect.left = 0; windowRect.right = DEFAULT_WIDTH; windowRect.top = 0; windowRect.bottom = DEFAULT_HEIGHT; AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); *hWnd = CreateWindowEx(dwExStyle, "Zoom" /* Class Name */, "Zoom" /* Title*/, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle, 0, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, 0, 0, hInstance, 0); if(!*hWnd) { MessageBox(0, "Window Creation Error.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor 1, // Version Number PFD_DRAW_TO_WINDOW | // Format Must Support Window PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, // Must Support Double Buffering PFD_TYPE_RGBA, // Request An RGBA Format 16, // Select Our Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored 0, // No Alpha Buffer 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored 16, // 16Bit Z-Buffer (Depth Buffer) 0, // No Stencil Buffer 0, // No Auxiliary Buffer PFD_MAIN_PLANE, // Main Drawing Layer 0, // Reserved 0, 0, 0 // Layer Masks Ignored }; *hDC=GetDC(*hWnd); if (!*hDC) { WGLUninit(*hWnd, 0, 0); MessageBox(0, "Can't Create A GL Device Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } GLuint pixelFormat = ChoosePixelFormat(*hDC, &pfd); if(!pixelFormat) { WGLUninit(*hWnd, *hDC, 0); MessageBox(0, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } if(!SetPixelFormat(*hDC, pixelFormat, &pfd)) { WGLUninit(*hWnd, *hDC, 0); MessageBox(0, "Can't Set The PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } *hRC=wglCreateContext(*hDC); if(!*hRC) { WGLUninit(*hWnd, *hDC, 0); MessageBox(0, "Can't Create A GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } if(!wglMakeCurrent(*hDC, *hRC)) { WGLUninit(*hWnd, *hDC, *hRC); MessageBox(0, "Can't Activate The GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); return false; } ShowWindow(*hWnd, SW_SHOW); SetForegroundWindow(*hWnd); SetFocus(*hWnd); return true; } void WGLUninit(HWND hWnd, HDC hDC, HGLRC hRC) { wglMakeCurrent(0, 0); if(hRC) wglDeleteContext(hRC); if(hDC) ReleaseDC(hWnd, hDC); if(hWnd) DestroyWindow(hWnd); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd = 0; HDC hDC = 0; HGLRC hRC = 0; if(!WGLInit(hInstance, &hWnd, &hDC, &hRC)) { return 0; } glewInit(); resize(DEFAULT_WIDTH, DEFAULT_HEIGHT); bool running = true; MSG msg; while(running) { unsigned long delta = 0; unsigned long ticks = GetTickCount(); while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if(msg.message == WM_QUIT) { running = false; break; } else // If Not, Deal With Window Messages { TranslateMessage(&msg); // Translate The Message DispatchMessage(&msg); // Dispatch The Message } } delta = GetTickCount()-ticks; if(delta < MIN_FRAME_DELTA) { Sleep(MIN_FRAME_DELTA - delta); delta = GetTickCount()-ticks; } ticks += delta; static DisplayClass render; render.renderScene(delta); SwapBuffers(hDC); } WGLUninit(hWnd, hDC, hRC); return msg.wParam; } LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; case WM_SIZE: resize(LOWORD(lParam), HIWORD(lParam)); return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } #else bool XIInit(Display *disp, int *opcode) { int event, error; if (!XQueryExtension(disp, "XInputExtension", opcode, &event, &error)) { std::cerr << "X Input extension not available." << std::endl; return false; } int major = 2, minor = 0; if (XIQueryVersion(disp, &major, &minor) == BadRequest) { std::cerr << "XI2 not available. Server supports " << major << "." << minor << std::endl; return false; } return true; } static Bool WaitForMapNotify(Display*, XEvent *event, XPointer arg) { return (event->type == MapNotify && event->xmap.window == (Window)arg); } bool GLXinit(Display *disp, Atom windele, Window *wnd, GLXContext *gc, int *xi_opcode, int *pointer) { static const int msAttributeList[] = {GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_X_RENDERABLE, True, GLX_DOUBLEBUFFER, True, GLX_DEPTH_SIZE, 1, GLX_STENCIL_SIZE, 1, GLX_SAMPLE_BUFFERS, 1, GLX_SAMPLES, 4, None}; static const int attributeList[] = {GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_X_RENDERABLE, True, GLX_DOUBLEBUFFER, True, GLX_DEPTH_SIZE, 1, GLX_STENCIL_SIZE, 1, None}; if(!XIInit(disp, xi_opcode)) return false; bool ok = false; int nElements; GLXFBConfig *fbConfig = glXChooseFBConfig(disp, DefaultScreen(disp), msAttributeList, &nElements); if(!fbConfig || !nElements) { if(fbConfig) { XFree(fbConfig); } fbConfig = glXChooseFBConfig(disp, DefaultScreen(disp), attributeList, &nElements); } if(fbConfig && nElements) { XVisualInfo *vi = glXGetVisualFromFBConfig(disp, *fbConfig); if(vi) { Colormap cmap = XCreateColormap(disp, RootWindow(disp, vi->screen), vi->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask/* | PointerMotionMask | ButtonPressMask | ButtonReleaseMask*/; *wnd = XCreateWindow(disp, RootWindow(disp, vi->screen), 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); XClassHint chint; chint.res_name = const_cast("Zoom"); chint.res_class = const_cast("zoom"); XSetClassHint(disp, *wnd, &chint); char *str = const_cast("Zoom"); XTextProperty name; if(XStringListToTextProperty(&str, 1, &name) != 0) { XSetWMName(disp, *wnd, &name); XFree(name.value); } XSetWMProtocols(disp, *wnd, &windele, 1); XColor color; color.pixel = WhitePixel(disp, vi->screen); XQueryColor(disp, cmap, &color); Pixmap pixmap = XCreatePixmap(disp, *wnd, 1, 1, 1); Cursor cursor = XCreatePixmapCursor(disp, pixmap, pixmap, &color, &color, 0, 0); XSync(disp, False); XFreePixmap(disp, pixmap); XMapWindow(disp, *wnd); XEvent event; XIfEvent(disp, &event, WaitForMapNotify, (XPointer)*wnd); XIGetClientPointer(disp, *wnd, pointer); XIEventMask eventmask; unsigned char mask[4] = {0}; eventmask.deviceid = 2; eventmask.mask_len = sizeof(mask); eventmask.mask = mask; /*XISetMask(mask, XI_ButtonPress); XISetMask(mask, XI_Motion); XISetMask(mask, XI_KeyPress);*/ XISetMask(mask, XI_RawMotion); XIGrabDevice(disp, *pointer, *wnd, CurrentTime, cursor, GrabModeAsync, GrabModeAsync, True, &eventmask); *gc = glXCreateContext(disp, vi, NULL, True); glXMakeCurrent(disp, *wnd, *gc); ok = true; XFree(vi); } } if(fbConfig) XFree(fbConfig); XSync(disp, 0); return ok; } int main() { Display *disp = XOpenDisplay(0); Atom windele = XInternAtom(disp, "WM_DELETE_WINDOW", False); Window wnd; GLXContext gc; int xi_opcode; int pointer; if(!GLXinit(disp, windele, &wnd, &gc, &xi_opcode, &pointer)) return 1; glewInit(); Zoom::Game game; game.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT); GLint samples; glGetIntegerv(GL_SAMPLES, &samples); std::cerr << "Using " << samples << " samples" << std::endl; bool running = true; unsigned long delta = 0; unsigned long frames = 0, tocks = 0; unsigned input = 0; struct timeval tv; gettimeofday(&tv, NULL); unsigned long ticks = tv.tv_usec; while(running) { while(XPending(disp)) { XEvent event; XNextEvent(disp, &event); switch(event.type) { case ConfigureNotify: game.resize(event.xconfigure.width, event.xconfigure.height); break; case ClientMessage: if(static_cast(event.xclient.data.l[0]) == windele) running = false; break; case KeyPress: switch(XkbKeycodeToKeysym(disp, event.xkey.keycode, 0, 0)) { case XK_Up: case XK_w: input |= Zoom::Game::FORWARD; break; case XK_Down: case XK_s: input |= Zoom::Game::BACKWARD; break; case XK_Left: case XK_a: input |= Zoom::Game::LEFT; break; case XK_Right: case XK_d: input |= Zoom::Game::RIGHT; break; case XK_Escape: running = false; break; } break; case KeyRelease: switch(XkbKeycodeToKeysym(disp, event.xkey.keycode, 0, 0)) { case XK_Up: case XK_w: input &= ~Zoom::Game::FORWARD; break; case XK_Down: case XK_s: input &= ~Zoom::Game::BACKWARD; break; case XK_Left: case XK_a: input &= ~Zoom::Game::LEFT; break; case XK_Right: case XK_d: input &= ~Zoom::Game::RIGHT; break; } break; case GenericEvent: if(event.xcookie.extension == xi_opcode && XGetEventData(disp, &event.xcookie)) { switch(event.xcookie.evtype) { case XI_RawMotion: XIRawEvent *rawEvent = reinterpret_cast(event.xcookie.data); float deltaX = 0, deltaY = 0; double *rawValuator = rawEvent->raw_values; if(rawEvent->valuators.mask_len >= 1) { if(XIMaskIsSet(rawEvent->valuators.mask, 0)) { deltaX = (float)*rawValuator; ++rawValuator; } if(XIMaskIsSet(rawEvent->valuators.mask, 1)) { deltaY = (float)*rawValuator; ++rawValuator; } } if(deltaX != 0 || deltaY != 0) game.turn(deltaX, deltaY); } XFreeEventData(disp, &event.xcookie); } } } if(!running) break; game.setInput(input); game.run(delta); game.render(); glXSwapBuffers(disp, wnd); XSync(disp, 0); long slept = 0; gettimeofday(&tv, NULL); delta = ((tv.tv_usec + 1000000 - ticks)%1000000)/1000; if(delta < MIN_FRAME_DELTA) { usleep((MIN_FRAME_DELTA-delta)*1000); slept += (MIN_FRAME_DELTA-delta); gettimeofday(&tv, NULL); delta = ((tv.tv_usec + 1000000 - ticks)%1000000)/1000; } ticks = (ticks + delta*1000) % 1000000; frames++; tocks += delta*1000; if(tocks > 1000000) { std::cerr << frames << " fps; slept a total of " << slept << " ms" << std::endl; frames = 0; tocks -= 1000000; slept = 0; } } XIUngrabDevice(disp, pointer, CurrentTime); XDestroyWindow(disp, wnd); glXDestroyContext(disp, gc); XCloseDisplay(disp); return 0; } #endif