]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/linux/glimp.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sys / linux / glimp.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28 #include "../../idlib/precompiled.h"
29 #include "../../renderer/tr_local.h"
30 #include "local.h"
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36
37 extern "C" {
38 #       include "libXNVCtrl/NVCtrlLib.h"
39 }
40
41 idCVar sys_videoRam( "sys_videoRam", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Texture memory on the video card (in megabytes) - 0: autodetect", 0, 512 );
42
43 Display *dpy = NULL;
44 static int scrnum = 0;
45
46 Window win = 0;
47
48 bool dga_found = false;
49
50 static GLXContext ctx = NULL;
51
52 static bool vidmode_ext = false;
53 static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0;  // major and minor of XF86VidExtensions
54
55 static XF86VidModeModeInfo **vidmodes;
56 static int num_vidmodes;
57 static bool vidmode_active = false;
58
59 // backup gamma ramp
60 static int save_rampsize = 0;
61 static unsigned short *save_red, *save_green, *save_blue;
62
63 void GLimp_WakeBackEnd(void *a) {
64         common->DPrintf("GLimp_WakeBackEnd stub\n");
65 }
66
67 #ifdef ID_GL_HARDLINK
68 void GLimp_EnableLogging(bool log) {
69         static bool logging;
70         if (log != logging)
71         {
72                 common->DPrintf("GLimp_EnableLogging - disabled at compile time (ID_GL_HARDLINK)\n");
73                 logging = log;
74         }
75 }
76 #endif
77
78 void GLimp_FrontEndSleep() {
79         common->DPrintf("GLimp_FrontEndSleep stub\n");
80 }
81
82 void *GLimp_BackEndSleep() {
83         common->DPrintf("GLimp_BackEndSleep stub\n");
84         return 0;
85 }
86
87 bool GLimp_SpawnRenderThread(void (*a) ()) {
88         common->DPrintf("GLimp_SpawnRenderThread stub\n");
89         return false;
90 }
91
92 void GLimp_ActivateContext() {
93         assert( dpy );
94         assert( ctx );
95         qglXMakeCurrent( dpy, win, ctx );
96 }
97
98 void GLimp_DeactivateContext() {
99         assert( dpy );
100         qglXMakeCurrent( dpy, None, NULL );
101 }
102
103 /*
104 =================
105 GLimp_SaveGamma
106
107 save and restore the original gamma of the system
108 =================
109 */
110 void GLimp_SaveGamma() {
111         if ( save_rampsize ) {
112                 return;
113         }
114
115         assert( dpy );
116         
117         XF86VidModeGetGammaRampSize( dpy, scrnum, &save_rampsize);
118         save_red = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
119         save_green = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
120         save_blue = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
121         XF86VidModeGetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
122 }
123
124 /*
125 =================
126 GLimp_RestoreGamma
127
128 save and restore the original gamma of the system
129 =================
130 */
131 void GLimp_RestoreGamma() {
132         if (!save_rampsize)
133                 return;
134         
135         XF86VidModeSetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
136         
137         free(save_red); free(save_green); free(save_blue);
138         save_rampsize = 0;
139 }
140
141 /*
142 =================
143 GLimp_SetGamma
144
145 gamma ramp is generated by the renderer from r_gamma and r_brightness for 256 elements
146 the size of the gamma ramp can not be changed on X (I need to confirm this)
147 =================
148 */
149 void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned short blue[256]) {
150         if ( dpy ) {            
151                 int size;
152                 
153                 GLimp_SaveGamma();
154                 XF86VidModeGetGammaRampSize( dpy, scrnum, &size);
155                 common->DPrintf("XF86VidModeGetGammaRampSize: %d\n", size);
156                 if ( size > 256 ) {
157                         // silly generic resample
158                         int i;
159                         unsigned short *l_red, *l_green, *l_blue;
160                         l_red = (unsigned short *)malloc(size*sizeof(unsigned short));
161                         l_green = (unsigned short *)malloc(size*sizeof(unsigned short));
162                         l_blue = (unsigned short *)malloc(size*sizeof(unsigned short));
163                         //int r_size = 256;
164                         int r_i; float r_f;
165                         for(i=0; i<size-1; i++) {
166                                 r_f = (float)i*255.0f/(float)(size-1);
167                                 r_i = (int)floor(r_f);
168                                 r_f -= (float)r_i;
169                                 l_red[i] = (int)round((1.0f-r_f)*(float)red[r_i]+r_f*(float)red[r_i+1]);
170                                 l_green[i] = (int)round((1.0f-r_f)*(float)green[r_i]+r_f*(float)green[r_i+1]);
171                                 l_blue[i] = (int)round((1.0f-r_f)*(float)blue[r_i]+r_f*(float)blue[r_i+1]);                             
172                         }
173                         l_red[size-1] = red[255]; l_green[size-1] = green[255]; l_blue[size-1] = blue[255];
174                         XF86VidModeSetGammaRamp( dpy, scrnum, size, l_red, l_green, l_blue );
175                         free(l_red); free(l_green); free(l_blue);
176                 } else {
177                         XF86VidModeSetGammaRamp( dpy, scrnum, size, red, green, blue );
178                 }
179         }
180 }
181
182 void GLimp_Shutdown() {
183         if ( dpy ) {
184                 
185                 Sys_XUninstallGrabs();
186         
187                 GLimp_RestoreGamma();
188
189                 qglXDestroyContext( dpy, ctx );
190                 
191 #if !defined( ID_GL_HARDLINK )
192                 GLimp_dlclose();
193 #endif
194                 
195                 XDestroyWindow( dpy, win );
196                 if ( vidmode_active ) {
197                         XF86VidModeSwitchToMode( dpy, scrnum, vidmodes[0] );
198                 }
199
200                 XFlush( dpy );
201
202                 // FIXME: that's going to crash
203                 //XCloseDisplay( dpy );
204
205                 vidmode_active = false;
206                 dpy = NULL;
207                 win = 0;
208                 ctx = NULL;
209         }
210 }
211
212 void GLimp_SwapBuffers() {
213         assert( dpy );
214         qglXSwapBuffers( dpy, win );
215 }
216
217 /*
218 GLX_TestDGA
219 Check for DGA   - update in_dgamouse if needed
220 */
221 void GLX_TestDGA() {
222         int dga_MajorVersion = 0, dga_MinorVersion = 0;
223
224         assert( dpy );
225
226 #if defined( ID_ENABLE_DGA )
227         if ( !XF86DGAQueryVersion( dpy, &dga_MajorVersion, &dga_MinorVersion ) ) {
228                 // unable to query, probalby not supported
229                 common->Printf( "Failed to detect DGA DirectVideo Mouse\n" );
230                 cvarSystem->SetCVarBool( "in_dgamouse", false );
231                 dga_found = false;
232         } else {
233                 common->Printf( "DGA DirectVideo Mouse (Version %d.%d) initialized\n",
234                                    dga_MajorVersion, dga_MinorVersion );
235                 dga_found = true;
236         }
237 #else
238     dga_found = false;
239 #endif
240 }
241
242 /*
243 ** XErrorHandler
244 **   the default X error handler exits the application
245 **   I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest)
246 **   but those don't seem to be fatal .. so the default would be to just ignore them
247 **   our implementation mimics the default handler behaviour (not completely cause I'm lazy)
248 */
249 int idXErrorHandler(Display * l_dpy, XErrorEvent * ev) {
250         char buf[1024];
251         common->Printf( "Fatal X Error:\n" );
252         common->Printf( "  Major opcode of failed request: %d\n", ev->request_code );
253         common->Printf( "  Minor opcode of failed request: %d\n", ev->minor_code );
254         common->Printf( "  Serial number of failed request: %lu\n", ev->serial );
255         XGetErrorText( l_dpy, ev->error_code, buf, 1024 );
256         common->Printf( "%s\n", buf );
257         return 0;
258 }
259
260 bool GLimp_OpenDisplay( void ) {
261         if ( dpy ) {
262                 return true;
263         }
264
265         if ( cvarSystem->GetCVarInteger( "net_serverDedicated" ) == 1 ) {
266                 common->DPrintf( "not opening the display: dedicated server\n" );
267                 return false;
268         }
269
270         common->Printf( "Setup X display connection\n" );
271
272         // that should be the first call into X
273         if ( !XInitThreads() ) {
274                 common->Printf("XInitThreads failed\n");
275                 return false;
276         }
277         
278         // set up our custom error handler for X failures
279         XSetErrorHandler( &idXErrorHandler );
280
281         if ( !( dpy = XOpenDisplay(NULL) ) ) {
282                 common->Printf( "Couldn't open the X display\n" );
283                 return false;
284         }
285         scrnum = DefaultScreen( dpy );
286         return true;
287 }
288
289 /*
290 ===============
291 GLX_Init
292 ===============
293 */
294 int GLX_Init(glimpParms_t a) {
295         int attrib[] = {
296                 GLX_RGBA,                               // 0
297                 GLX_RED_SIZE, 8,                // 1, 2
298                 GLX_GREEN_SIZE, 8,              // 3, 4
299                 GLX_BLUE_SIZE, 8,               // 5, 6
300                 GLX_DOUBLEBUFFER,               // 7
301                 GLX_DEPTH_SIZE, 24,             // 8, 9
302                 GLX_STENCIL_SIZE, 8,    // 10, 11
303                 GLX_ALPHA_SIZE, 8, // 12, 13
304                 None
305         };
306         // these match in the array
307 #define ATTR_RED_IDX 2
308 #define ATTR_GREEN_IDX 4
309 #define ATTR_BLUE_IDX 6
310 #define ATTR_DEPTH_IDX 9
311 #define ATTR_STENCIL_IDX 11
312 #define ATTR_ALPHA_IDX 13
313         Window root;
314         XVisualInfo *visinfo;
315         XSetWindowAttributes attr;
316         XSizeHints sizehints;
317         unsigned long mask;
318         int colorbits, depthbits, stencilbits;
319         int tcolorbits, tdepthbits, tstencilbits;
320         int actualWidth, actualHeight;
321         int i;
322         const char *glstring;
323
324         if ( !GLimp_OpenDisplay() ) {
325                 return false;
326         }
327
328         common->Printf( "Initializing OpenGL display\n" );
329
330         root = RootWindow( dpy, scrnum );
331
332         actualWidth = glConfig.vidWidth;
333         actualHeight = glConfig.vidHeight;
334
335         // Get video mode list
336         if ( !XF86VidModeQueryVersion( dpy, &vidmode_MajorVersion, &vidmode_MinorVersion ) ) {
337                 vidmode_ext = false;
338                 common->Printf("XFree86-VidModeExtension not available\n");
339         } else {
340                 vidmode_ext = true;
341                 common->Printf("Using XFree86-VidModeExtension Version %d.%d\n",
342                                    vidmode_MajorVersion, vidmode_MinorVersion);
343         }
344
345         GLX_TestDGA();
346
347         if ( vidmode_ext ) {
348                 int best_fit, best_dist, dist, x, y;
349
350                 XF86VidModeGetAllModeLines( dpy, scrnum, &num_vidmodes, &vidmodes );
351
352                 // Are we going fullscreen?  If so, let's change video mode
353                 if ( a.fullScreen ) {
354                         best_dist = 9999999;
355                         best_fit = -1;
356
357                         for (i = 0; i < num_vidmodes; i++) {
358                                 if (a.width > vidmodes[i]->hdisplay ||
359                                         a.height > vidmodes[i]->vdisplay)
360                                         continue;
361
362                                 x = a.width - vidmodes[i]->hdisplay;
363                                 y = a.height - vidmodes[i]->vdisplay;
364                                 dist = (x * x) + (y * y);
365                                 if (dist < best_dist) {
366                                         best_dist = dist;
367                                         best_fit = i;
368                                 }
369                         }
370
371                         if (best_fit != -1) {
372                                 actualWidth = vidmodes[best_fit]->hdisplay;
373                                 actualHeight = vidmodes[best_fit]->vdisplay;
374
375                                 // change to the mode
376                                 XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
377                                 vidmode_active = true;
378
379                                 // Move the viewport to top left
380                                 // FIXME: center?
381                                 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
382
383                                 common->Printf( "Free86-VidModeExtension Activated at %dx%d\n", actualWidth, actualHeight );
384
385                         } else {
386                                 a.fullScreen = false;
387                                 common->Printf( "Free86-VidModeExtension: No acceptable modes found\n" );
388                         }
389                 } else {
390                         common->Printf( "XFree86-VidModeExtension: not fullscreen, ignored\n" );
391                 }
392         }
393         // color, depth and stencil
394         colorbits = 24;
395         depthbits = 24;
396         stencilbits = 8;
397
398         for (i = 0; i < 16; i++) {
399                 // 0 - default
400                 // 1 - minus colorbits
401                 // 2 - minus depthbits
402                 // 3 - minus stencil
403                 if ((i % 4) == 0 && i) {
404                         // one pass, reduce
405                         switch (i / 4) {
406                         case 2:
407                                 if (colorbits == 24)
408                                         colorbits = 16;
409                                 break;
410                         case 1:
411                                 if (depthbits == 24)
412                                         depthbits = 16;
413                                 else if (depthbits == 16)
414                                         depthbits = 8;
415                         case 3:
416                                 if (stencilbits == 24)
417                                         stencilbits = 16;
418                                 else if (stencilbits == 16)
419                                         stencilbits = 8;
420                         }
421                 }
422
423                 tcolorbits = colorbits;
424                 tdepthbits = depthbits;
425                 tstencilbits = stencilbits;
426
427                 if ((i % 4) == 3) {             // reduce colorbits
428                         if (tcolorbits == 24)
429                                 tcolorbits = 16;
430                 }
431
432                 if ((i % 4) == 2) {             // reduce depthbits
433                         if (tdepthbits == 24)
434                                 tdepthbits = 16;
435                         else if (tdepthbits == 16)
436                                 tdepthbits = 8;
437                 }
438
439                 if ((i % 4) == 1) {             // reduce stencilbits
440                         if (tstencilbits == 24)
441                                 tstencilbits = 16;
442                         else if (tstencilbits == 16)
443                                 tstencilbits = 8;
444                         else
445                                 tstencilbits = 0;
446                 }
447
448                 if (tcolorbits == 24) {
449                         attrib[ATTR_RED_IDX] = 8;
450                         attrib[ATTR_GREEN_IDX] = 8;
451                         attrib[ATTR_BLUE_IDX] = 8;
452                 } else {
453                         // must be 16 bit
454                         attrib[ATTR_RED_IDX] = 4;
455                         attrib[ATTR_GREEN_IDX] = 4;
456                         attrib[ATTR_BLUE_IDX] = 4;
457                 }
458                 
459                 attrib[ATTR_DEPTH_IDX] = tdepthbits;    // default to 24 depth
460                 attrib[ATTR_STENCIL_IDX] = tstencilbits;
461
462                 visinfo = qglXChooseVisual(dpy, scrnum, attrib);
463                 if (!visinfo) {
464                         continue;
465                 }
466
467                 common->Printf( "Using %d/%d/%d Color bits, %d Alpha bits, %d depth, %d stencil display.\n",
468                          attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX],
469                          attrib[ATTR_BLUE_IDX], attrib[ATTR_ALPHA_IDX],
470                          attrib[ATTR_DEPTH_IDX],
471                          attrib[ATTR_STENCIL_IDX]);
472
473                 glConfig.colorBits = tcolorbits;
474                 glConfig.depthBits = tdepthbits;
475                 glConfig.stencilBits = tstencilbits;
476                 break;
477         }
478
479         if (!visinfo) {
480                 common->Printf("Couldn't get a visual\n");
481                 return false;
482         }
483         // window attributes
484         attr.background_pixel = BlackPixel(dpy, scrnum);
485         attr.border_pixel = 0;
486         attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
487         attr.event_mask = X_MASK;
488         if (vidmode_active) {
489                 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
490                         CWEventMask | CWOverrideRedirect;
491                 attr.override_redirect = True;
492                 attr.backing_store = NotUseful;
493                 attr.save_under = False;
494         } else {
495                 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
496         }
497
498         win = XCreateWindow(dpy, root, 0, 0,
499                                                 actualWidth, actualHeight,
500                                                 0, visinfo->depth, InputOutput,
501                                                 visinfo->visual, mask, &attr);
502
503         XStoreName(dpy, win, GAME_NAME);
504
505         // don't let the window be resized
506         // FIXME: allow resize (win32 does)
507         sizehints.flags = PMinSize | PMaxSize;
508         sizehints.min_width = sizehints.max_width = actualWidth;
509         sizehints.min_height = sizehints.max_height = actualHeight;
510
511         XSetWMNormalHints(dpy, win, &sizehints);
512
513         XMapWindow( dpy, win );
514
515         if ( vidmode_active ) {
516                 XMoveWindow( dpy, win, 0, 0 );
517         }
518
519         XFlush(dpy);
520         XSync(dpy, False);
521         ctx = qglXCreateContext(dpy, visinfo, NULL, True);
522         XSync(dpy, False);
523
524         // Free the visinfo after we're done with it
525         XFree(visinfo);
526
527         qglXMakeCurrent(dpy, win, ctx);
528
529         glstring = (const char *) qglGetString(GL_RENDERER);
530         common->Printf("GL_RENDERER: %s\n", glstring);
531         
532         glstring = (const char *) qglGetString(GL_EXTENSIONS);
533         common->Printf("GL_EXTENSIONS: %s\n", glstring);
534
535         // FIXME: here, software GL test
536
537         glConfig.isFullscreen = a.fullScreen;
538         
539         if ( glConfig.isFullscreen ) {
540                 Sys_GrabMouseCursor( true );
541         }
542         
543         return true;
544 }
545
546 /*
547 ===================
548 GLimp_Init
549
550 This is the platform specific OpenGL initialization function.  It
551 is responsible for loading OpenGL, initializing it,
552 creating a window of the appropriate size, doing
553 fullscreen manipulations, etc.  Its overall responsibility is
554 to make sure that a functional OpenGL subsystem is operating
555 when it returns to the ref.
556
557 If there is any failure, the renderer will revert back to safe
558 parameters and try again.
559 ===================
560 */
561 bool GLimp_Init( glimpParms_t a ) {
562
563         if ( !GLimp_OpenDisplay() ) {
564                 return false;
565         }
566         
567 #ifndef ID_GL_HARDLINK
568         if ( !GLimp_dlopen() ) {
569                 return false;
570         }
571 #endif
572         
573         if (!GLX_Init(a)) {
574                 return false;
575         }
576         
577         return true;
578 }
579
580 /*
581 ===================
582 GLimp_SetScreenParms
583 ===================
584 */
585 bool GLimp_SetScreenParms( glimpParms_t parms ) {
586         return true;
587 }
588
589 /*
590 ================
591 Sys_GetVideoRam
592 returns in megabytes
593 open your own display connection for the query and close it
594 using the one shared with GLimp_Init is not stable
595 ================
596 */
597 int Sys_GetVideoRam( void ) {
598         static int run_once = 0;
599         int major, minor, value;
600         Display *l_dpy;
601         int l_scrnum;
602
603         if ( run_once ) {
604                 return run_once;
605         }
606
607         if ( sys_videoRam.GetInteger() ) {
608                 run_once = sys_videoRam.GetInteger();
609                 return sys_videoRam.GetInteger();
610         }
611
612         // try a few strategies to guess the amount of video ram
613         common->Printf( "guessing video ram ( use +set sys_videoRam to force ) ..\n" );
614         if ( !GLimp_OpenDisplay( ) ) {
615                 run_once = 64;
616                 return run_once;
617         }
618         l_dpy = dpy;
619         l_scrnum = scrnum;
620         // go for nvidia ext first
621         if ( XNVCTRLQueryVersion( l_dpy, &major, &minor ) ) {
622                 common->Printf( "found XNVCtrl extension %d.%d\n", major, minor );
623                 if ( XNVCTRLIsNvScreen( l_dpy, l_scrnum ) ) {
624                         if ( XNVCTRLQueryAttribute( l_dpy, l_scrnum, 0, NV_CTRL_VIDEO_RAM, &value ) ) {
625                                 run_once = value / 1024;
626                                 return run_once;
627                         } else {
628                                 common->Printf( "XNVCtrlQueryAttribute NV_CTRL_VIDEO_RAM failed\n" );
629                         }
630                 } else {
631                         common->Printf( "default screen %d is not controlled by NVIDIA driver\n", l_scrnum );
632                 }
633         }
634         // try ATI /proc read ( for the lack of a better option )
635         int fd;
636         if ( ( fd = open( "/proc/dri/0/umm", O_RDONLY ) ) != -1 ) {
637                 int len;
638                 char umm_buf[ 1024 ];
639                 char *line;
640                 if ( ( len = read( fd, umm_buf, 1024 ) ) != -1 ) {
641                         // should be way enough to get the full file
642                         // grab "free  LFB = " line and "free  Inv = " lines
643                         umm_buf[ len-1 ] = '\0';
644                         line = umm_buf;
645                         line = strtok( umm_buf, "\n" );
646                         int total = 0;
647                         while ( line ) {
648                                 if ( strlen( line ) >= 13 && strstr( line, "max   LFB =" ) == line ) {
649                                         total += atoi( line + 12 );
650                                 } else if ( strlen( line ) >= 13 && strstr( line, "max   Inv =" ) == line ) {
651                                         total += atoi( line + 12 );
652                                 }
653                                 line = strtok( NULL, "\n" );
654                         }
655                         if ( total ) {
656                                 run_once = total / 1048576;
657                                 // round to the lower 16Mb
658                                 run_once &= ~15;
659                                 return run_once;
660                         }
661                 } else {
662                         common->Printf( "read /proc/dri/0/umm failed: %s\n", strerror( errno ) );
663                 }
664         }
665         common->Printf( "guess failed, return default low-end VRAM setting ( 64MB VRAM )\n" );
666         run_once = 64;
667         return run_once;
668 }