Initial commit.
[ccbs] / glwindow.cpp
1 #include <stdio.h>
2
3 #ifdef WIN32
4 #include <windows.h>
5 #endif
6
7 #ifdef __linux__
8 #include <unistd.h>
9 #include <GL/glx.h>
10 #include <X11/extensions/xf86vmode.h>
11 #include <X11/keysym.h>
12 #endif
13
14 #include <GL/gl.h>
15 #include <GL/glu.h>
16
17 #include "glwindow.h"
18
19 #ifdef WIN32
20 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
21
22 /* this is ugly, but so is Win32 ;-) */
23 GLWindow *win;
24 #endif
25
26 #define DEMOLIB_XASPECT 4.0f
27 #define DEMOLIB_YASPECT 3.0f
28
29 void GLWindow::resize(int x, int y, int w, int h)
30 {
31         /* Prevent division by zero */
32         if (h == 0) {
33                 h = 1;
34         }
35
36         float aspect = (float)w / (float)h;
37         if (aspect > DEMOLIB_XASPECT / DEMOLIB_YASPECT) {
38                 int new_w = (int)((float)h * DEMOLIB_XASPECT / DEMOLIB_YASPECT);
39                 x += (w - new_w) / 2;
40                 w = new_w;
41         } else if (aspect < DEMOLIB_XASPECT / DEMOLIB_YASPECT) {
42                 int new_h = (int)((float)w * DEMOLIB_YASPECT / DEMOLIB_XASPECT);
43                 y += (h - new_h) / 2;
44                 h = new_h;
45         }
46  
47         glViewport(x, y, w, h);
48
49 #ifdef __linux__
50 //      XClearWindow(this->dpy, this->win);
51 #endif
52 }
53
54 GLWindow::GLWindow(char *title, int width, int height, int bpp, bool fullscreen, int zbuffer, int visual_id)
55 {
56 #ifdef WIN32
57         GLuint PixelFormat;
58         WNDCLASS wc;
59         DWORD dwExStyle;
60         DWORD dwStyle;
61         DEVMODE dmScreenSettings;
62
63         if (visual_id != -1) {
64                 EnumDisplaySettings(NULL, visual_id, &dmScreenSettings);
65                 width = dmScreenSettings.dmPelsWidth;
66                 height = dmScreenSettings.dmPelsHeight;
67                 bpp = dmScreenSettings.dmBitsPerPel;
68         } else {
69                 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
70                 dmScreenSettings.dmSize = sizeof(dmScreenSettings);
71                 dmScreenSettings.dmPelsWidth = width;
72                 dmScreenSettings.dmPelsHeight = height;
73                 dmScreenSettings.dmBitsPerPel = bpp;
74                 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
75         }
76         win = this;
77         
78         RECT WindowRect;
79         WindowRect.left = (long)0;
80         WindowRect.right = (long)width;
81         WindowRect.top = (long)0;
82         WindowRect.bottom = (long)height;
83         
84 #endif /* WIN32 */      
85 #ifdef __linux__
86         XVisualInfo *vi;
87         int dpyWidth = 0, dpyHeight = 0;
88         int i;
89         XF86VidModeModeInfo **modes;
90         int modeNum;
91         int bestMode;
92         Atom wmDelete;
93         Window winDummy;
94         unsigned int borderDummy;
95
96         static int attrList[] = {
97                 GLX_RGBA, 
98                 GLX_RED_SIZE, 1,
99                 GLX_GREEN_SIZE, 1,
100                 GLX_BLUE_SIZE, 1,
101                 GLX_DOUBLEBUFFER,
102                 GLX_DEPTH_SIZE, zbuffer,
103                 GLX_STENCIL_SIZE, 4,
104                 None
105         };
106 #endif /* __linux__ */
107
108         this->x = 0;
109         this->y = 0;
110         this->width = width;
111         this->height = height;
112         this->bpp = bpp;
113         this->fullscreen = fullscreen;
114         this->zbuffer = zbuffer;
115         this->done = 0;
116         
117 #ifdef WIN32
118         this->hInstance = GetModuleHandle(NULL);
119         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
120         wc.lpfnWndProc = WndProc;
121         wc.cbClsExtra = 0;
122         wc.cbWndExtra = 0;
123         wc.hInstance = this->hInstance;
124         wc.hIcon = NULL;
125         wc.hCursor = NULL;
126         wc.hbrBackground = NULL;
127         wc.lpszMenuName = NULL;
128         wc.lpszClassName = "Excess-OGL";
129
130         if( !RegisterClass(&wc) ) throw "Couldn't register Window Class";
131
132 #endif /* WIN32 */
133 #ifdef __linux__
134         /* set best mode to current */
135         bestMode = 0;
136
137         /* get a connection */
138         this->dpy = XOpenDisplay(0);
139         this->screen = DefaultScreen(this->dpy);
140
141         if (fullscreen) {
142                 XF86VidModeGetAllModeLines(this->dpy, this->screen, &modeNum, &modes);
143
144                 /* save desktop-resolution before switching modes */
145                 this->deskMode = *modes[0];
146
147                 /* look for mode with requested resolution */
148                 for (i = 0; i < modeNum; i++) {
149                         if ((modes[i]->hdisplay == width) && (modes[i]->vdisplay == height)) {
150                                 bestMode = i;
151                         }
152                 }
153
154                 /* if we don't have it, bomb out */
155                 if (bestMode == 0 && (modes[0]->hdisplay != width || modes[0]->vdisplay != height)) {
156                         throw "Couldn't set requested screen mode.";
157                 }
158         }
159
160         if (visual_id != -1) {
161                 XVisualInfo tmplate;
162                 int nret;
163                 
164                 tmplate.visualid = visual_id;
165                 vi = XGetVisualInfo(this->dpy, VisualIDMask, &tmplate, &nret);
166                 if (vi == NULL) {
167                         throw "Couldn't get selected visual!";
168                 }
169         } else {
170                 /* get an appropriate visual */
171                 vi = glXChooseVisual(this->dpy, this->screen, attrList);
172                 if (vi == NULL) {
173                         throw "Couldn't get double-buffered visual";
174                 }
175         }
176
177         /* create a GLX context */
178         this->ctx = glXCreateContext(this->dpy, vi, NULL, GL_TRUE);
179
180         /* create a color map (umm, needed?) */
181         Colormap cmap = XCreateColormap(this->dpy, RootWindow(this->dpy, vi->screen),
182                 vi->visual, AllocNone);
183         this->attr.colormap = cmap;
184
185         /* make a blank cursor */
186         {
187                 static char data[1] = {0};
188                 Cursor cursor;
189                 Pixmap blank;
190                 XColor dummy;
191
192                 blank = XCreateBitmapFromData(this->dpy, RootWindow(this->dpy, vi->screen), data, 1, 1);
193                 if (blank == None)
194                         throw "Out of memory!";
195                 cursor = XCreatePixmapCursor(this->dpy, blank, blank, &dummy, &dummy, 0, 0);
196                 XFreePixmap(this->dpy, blank);
197                 this->attr.cursor = cursor;
198         }
199                 
200         this->attr.border_pixel = 0;
201 #endif /* __linux__ */
202
203         /* change screen mode */        
204         if (fullscreen) {
205 #ifdef WIN32
206                 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
207                         throw "Couldn't set requested screen mode.";
208                 }
209 #endif /* WIN32 */
210 #ifdef __linux__
211                 XF86VidModeSwitchToMode(this->dpy, this->screen, modes[bestMode]);
212                 XF86VidModeSetViewPort(this->dpy, this->screen, 0, 0);
213                 dpyWidth = modes[bestMode]->hdisplay;
214                 dpyHeight = modes[bestMode]->vdisplay;
215                 XFree(modes);
216 #endif /* __linux__ */
217         }
218
219         /* create the window */
220 #ifdef WIN32
221         if (fullscreen) {
222                 dwExStyle = WS_EX_APPWINDOW;
223                 dwStyle = WS_POPUP;
224                 ShowCursor(FALSE);
225         } else {
226                 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
227                 dwStyle = WS_OVERLAPPEDWINDOW;
228         }
229
230         AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
231
232         if (!(hWnd = CreateWindowEx(dwExStyle,
233                                     "Excess-OGL",
234                                     title,
235                                     dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
236                                     0, 0,
237                                     WindowRect.right - WindowRect.left,
238                                     WindowRect.bottom - WindowRect.top,
239                                     NULL,
240                                     NULL,
241                                     this->hInstance,
242                                     NULL))) {
243                 delete this;
244                 throw "Could not change screenmode";
245         }
246 #endif
247 #ifdef __linux__
248         this->attr.background_pixel = 0;
249
250         if (fullscreen) {
251                 /* create a fullscreen window */
252                 this->attr.override_redirect = True;
253                 this->attr.event_mask = KeyPressMask | ButtonPressMask |
254                                         StructureNotifyMask;
255
256                 this->win = XCreateWindow(this->dpy, RootWindow(this->dpy, vi->screen),
257                         0, 0, dpyWidth, dpyHeight, 0, vi->depth, InputOutput, vi->visual,
258                         CWColormap | CWCursor | CWEventMask | CWOverrideRedirect,
259                         &this->attr);
260                 XWarpPointer(this->dpy, None, this->win, 0, 0, 0, 0, 0, 0);
261                 XMapRaised(this->dpy, this->win);
262                 XGrabKeyboard(this->dpy, this->win, True, GrabModeAsync,
263                         GrabModeAsync, CurrentTime);
264                 XGrabPointer(this->dpy, this->win, True, ButtonPressMask,
265                         GrabModeAsync, GrabModeAsync, this->win, None, CurrentTime);
266         } else {
267                 /* create a window in window mode*/
268                 this->attr.event_mask = KeyPressMask | ButtonPressMask |
269                         StructureNotifyMask;
270                 this->win = XCreateWindow(this->dpy, RootWindow(this->dpy, vi->screen),
271                         0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
272                         CWColormap | CWBorderPixel | CWEventMask, &this->attr);
273
274                 /* only set window title and handle wm_delete_events if in windowed mode */
275                 wmDelete = XInternAtom(this->dpy, "WM_DELETE_WINDOW", True);
276                 XSetWMProtocols(this->dpy, this->win, &wmDelete, 1);
277                 XSetStandardProperties(this->dpy, this->win, title,
278                         title, None, NULL, 0, NULL);
279                 XMapRaised(this->dpy, this->win);
280         }
281 #endif /* __linux__ */
282
283 #ifdef WIN32
284         static PIXELFORMATDESCRIPTOR pfd = {
285                 sizeof(PIXELFORMATDESCRIPTOR),
286                 1,
287                 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
288                 PFD_TYPE_RGBA,
289                 bpp,
290                 0, 0, 0, 0, 0, 0,
291                 0,
292                 0,
293                 0,
294                 0, 0, 0, 0,
295                 zbuffer,
296                 8,
297                 0,
298                 PFD_MAIN_PLANE,
299                 0,
300                 0, 0, 0
301         };
302         
303         hDC = GetDC(hWnd);
304         PixelFormat = ChoosePixelFormat(hDC, &pfd);
305         if (PixelFormat == 0) {
306                 throw "Could not find a usable pixelformat";
307         }
308         SetPixelFormat(hDC, PixelFormat, &pfd);
309         hRC = wglCreateContext(hDC);
310         wglMakeCurrent(hDC, hRC);
311         ShowWindow(hWnd, SW_SHOW);
312         SetForegroundWindow(hWnd);
313         SetFocus(hWnd);
314
315         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
316 #endif /* WIN32 */
317 #ifdef __linux__
318         /* connect the glx-context to the window */
319         glXMakeCurrent(this->dpy, this->win, this->ctx);
320         XClearWindow(this->dpy, this->win);
321         XGetGeometry(this->dpy, this->win, &winDummy, &this->x, &this->y,
322                 &this->width, &this->height, &borderDummy, &this->bpp);
323         if (!glXIsDirect(this->dpy, this->ctx)) {
324                 throw "No direct rendering (hardware acceleration) available!";
325         }
326
327         nice(-7);
328 #endif /* __linux__ */
329
330         this->resize(0, 0, this->width, this->height);
331 }
332
333 GLWindow::~GLWindow()
334 {
335 #ifdef __linux__
336         if (this->ctx) {
337                 if (!glXMakeCurrent(this->dpy, None, NULL)) {
338                         throw "Could not release drawing context.";
339                 }
340                 glXDestroyContext(this->dpy, this->ctx);
341                 this->ctx = NULL;
342         }
343 #endif
344
345         if (fullscreen) {
346 #ifdef __linux__
347                 XF86VidModeSwitchToMode(this->dpy, this->screen, &this->deskMode);
348                 XF86VidModeSetViewPort(this->dpy, this->screen, 0, 0);
349 #endif
350 #ifdef WIN32
351                 ChangeDisplaySettings(NULL,0);
352                 ShowCursor(TRUE);
353 #endif
354         }
355
356 #ifdef __linux__
357         XCloseDisplay(this->dpy);
358 #endif
359
360 #ifdef WIN32
361         if (hRC) {
362                 wglMakeCurrent(NULL, NULL);
363                 wglDeleteContext(hRC);
364                 hRC = NULL;
365         }
366         
367         if (hDC != NULL && ReleaseDC(hWnd, hDC)) hDC = NULL;
368         if (hWnd != NULL && DestroyWindow(hWnd)) hWnd = NULL;
369         UnregisterClass("Excess-OGL", hInstance);
370 #endif
371 }
372
373 void GLWindow::flip()
374 {
375 #ifdef WIN32
376         MSG msg;
377         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
378                 if (msg.message == WM_QUIT) {
379                         this->done = TRUE;
380                 } else {
381                         TranslateMessage(&msg);
382                         DispatchMessage(&msg);
383                 }
384         }
385         SwapBuffers(this->hDC);
386 #endif
387 #ifdef __linux__
388         glXSwapBuffers(this->dpy, this->win);
389 #endif
390 }
391
392 bool GLWindow::is_done()
393 {
394         return this->done;
395 }
396
397 #ifdef WIN32
398 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
399 {
400         switch (uMsg) {
401         case WM_SYSCOMMAND:
402                 switch (wParam) {
403                 case SC_SCREENSAVE:
404                 case SC_MONITORPOWER:
405                         return 0;
406                 }
407                 break;
408
409         case WM_CLOSE:
410                 PostQuitMessage(0);
411                 return 0;
412
413         case WM_KEYUP:
414                 if (wParam == VK_ESCAPE)
415                         PostQuitMessage(0);
416                 return 0;
417
418         case WM_SIZE:
419                 win->resize(0, 0, LOWORD(lParam), HIWORD(lParam));
420                 return 0;
421         }
422
423         return DefWindowProc(hWnd, uMsg, wParam, lParam);
424 }
425 #endif //WIN32