Round 1: $HOME/.hhexen/ for configs and savegames.
[theoddone33/hhexen.git] / opengl / i_gl.cpp
1 //**************************************************************************
2 //**
3 //** $Id$
4 //**
5 //**************************************************************************
6
7
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <unistd.h>
13 #include <sys/time.h>
14 #include <X11/keysym.h>
15 #include "x11window.h"
16 #include <GL/gl.h>
17 #include <GL/glu.h>
18 #include <GL/glx.h>
19
20
21 extern "C" {
22 #include "h2def.h"
23
24 extern void OGL_InitData();
25 extern void OGL_InitRenderer();
26 extern void OGL_ResetData();    
27 extern void OGL_ResetLumpTexData();
28 }
29
30
31 void OGL_GrabScreen();
32
33
34 class HexenWindow : public X11Window
35 {
36 public:
37
38     HexenWindow();
39
40     ~HexenWindow();
41
42     void swap() { glXSwapBuffers( display(), window() ); }
43
44     GLXContext context() { return _ctx; }
45
46     void ungrabPointer()
47     {
48         XUngrabPointer( display(), CurrentTime );
49         showCursor();
50         _grabCursor = false;
51     }
52
53     void grabPointer()
54     {
55         XGrabPointer( display(), window(), True,
56                 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
57                 GrabModeAsync, GrabModeAsync, window(), None, CurrentTime );
58         hideCursor();
59         _grabCursor = true;
60     }
61
62 protected:
63
64     virtual void unknownEvent( XEvent* );
65     virtual void configureEvent( XConfigureEvent* );
66     virtual void deleteEvent( XEvent* );
67     virtual void buttonDown( XButtonEvent* );
68     virtual void buttonUp( XButtonEvent* );
69     virtual void motionEvent( XMotionEvent* );
70     virtual void keyDown( XKeyEvent* );
71     virtual void keyUp( XKeyEvent* );
72     virtual void exposeEvent( XExposeEvent* );
73
74 private:
75
76     void postKey( evtype_t type, KeySym key );
77     void postMouseEvent( int dx, int dy );
78  
79     GLXContext _ctx;
80     XVisualInfo* _vinfo;
81  
82     int _prevX, _prevY;
83     int _buttons;
84     bool _grabCursor;
85 };
86
87
88 HexenWindow* _win;
89
90
91 extern "C" {
92
93
94 // Public Data
95
96 int screenWidth  = SCREENWIDTH*2;
97 int screenHeight = SCREENHEIGHT*2;
98 int maxTexSize = 256;
99 int ratioLimit = 0;             // Zero if none.
100 int test3dfx = 0;
101
102 int DisplayTicker = 0;
103
104 extern int ticcount;
105
106 extern boolean novideo; // if true, stay in text mode for debugging
107
108
109 //==================================================
110
111 #define MOUSEB1 1
112 #define MOUSEB2 2
113 #define MOUSEB3 4
114
115 //==================================================
116
117 #define KEY_TAB         9   // From am_map.h
118
119 #define KEY_INS         0x52
120 #define KEY_DEL         0x53
121 #define KEY_PGUP        0x49
122 #define KEY_PGDN        0x51
123 #define KEY_HOME        0x47
124 #define KEY_END         0x4f
125
126
127 /*
128 ============================================================================
129
130                                                                 USER INPUT
131
132 ============================================================================
133 */
134
135 //--------------------------------------------------------------------------
136 //
137 // PROC I_WaitVBL
138 //
139 //--------------------------------------------------------------------------
140
141 void I_WaitVBL(int vbls)
142 {
143         if( novideo )
144         {
145                 return;
146         }
147         while( vbls-- )
148         {
149         usleep( 16667 );
150         }
151 }
152
153 //--------------------------------------------------------------------------
154 //
155 // PROC I_SetPalette
156 //
157 // Palette source must use 8 bit RGB elements.
158 //
159 //--------------------------------------------------------------------------
160
161 void I_SetPalette( byte *palette )
162 {
163 }
164
165 /*
166 ============================================================================
167
168                                                         GRAPHICS MODE
169
170 ============================================================================
171 */
172
173 /*
174 ==============
175 =
176 = I_Update
177 =
178 ==============
179 */
180
181 int UpdateState;
182 extern int screenblocks;
183
184 void I_Update (void)
185 {
186         if( UpdateState == I_NOUPDATE )
187         return;
188
189     _win->swap();
190
191         UpdateState = I_NOUPDATE; // clear out all draw types
192 }
193
194
195 //--------------------------------------------------------------------------
196 //
197 // PROC I_InitGraphics
198 //
199 //--------------------------------------------------------------------------
200
201 void I_InitGraphics(void)
202 {
203         int p;
204
205         if( novideo )
206         {
207                 return;
208         }
209     p = M_CheckParm( "-height" );
210     if (p && p < myargc - 1)
211     {
212         screenHeight = atoi(myargv[p+1]);
213     }
214     p = M_CheckParm( "-width" );
215     if( p && p < myargc - 1 )
216     {
217         screenWidth = atoi(myargv[p+1]);
218     }
219     ST_Message("Screen Width %d, Screen Height %d\n",screenWidth, screenHeight);    
220     _win = new HexenWindow();
221     if( ! _win )
222     {
223         exit( 3 );
224     }
225
226     //OGL_InitData();     // JHexen has this at the end of R_Init().
227         OGL_InitRenderer();
228
229         // Clear the buffers.
230         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
231     _win->swap();
232
233         // Print some OpenGL information.
234         ST_Message( "I_InitGraphics: OpenGL information:\n" );
235         ST_Message( "  Vendor: %s\n", glGetString(GL_VENDOR) );
236         ST_Message( "  Renderer: %s\n", glGetString(GL_RENDERER) );
237         ST_Message( "  Version: %s\n", glGetString(GL_VERSION) );
238         ST_Message( "  GLU Version: %s\n", gluGetString((GLenum)GLU_VERSION) );
239
240         // Check the maximum texture size.
241         glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTexSize );
242         ST_Message("  Maximum texture size: %d\n", maxTexSize);
243         if( maxTexSize == 256 )
244         {
245                 //ST_Message("  Is this Voodoo? Using size ratio limit.\n");
246                 ratioLimit = 8;
247         }
248
249         if( M_CheckParm("-3dfxtest") )
250         {
251                 test3dfx = 1;
252                 ST_Message("  3dfx test mode.\n");
253         }
254
255     _win->show();
256
257         //I_SetPalette( (byte*) W_CacheLumpName("PLAYPAL", PU_CACHE) );
258     _win->grabPointer ();
259 }
260
261 //--------------------------------------------------------------------------
262 //
263 // PROC I_ShutdownGraphics
264 //
265 //--------------------------------------------------------------------------
266
267 void I_ShutdownGraphics(void)
268 {
269         OGL_ResetData();
270         OGL_ResetLumpTexData();
271 }
272
273 //--------------------------------------------------------------------------
274 //
275 // PROC I_ReadScreen
276 //
277 // Reads the screen currently displayed into a linear buffer.
278 //
279 //--------------------------------------------------------------------------
280
281 /*
282 void I_ReadScreen(byte *scr)
283 {
284         memcpy(scr, screen, SCREENWIDTH*SCREENHEIGHT);
285 }
286 */
287
288 //===========================================================================
289
290
291 void I_StartTic (void)
292 {
293     // Handle keyboard & mouse events.
294
295     while( _win->eventsPending() )
296         _win->handleNextEvent();
297
298         //I_ReadMouse();
299 }
300
301
302 /*
303 ============================================================================
304
305                                         TIMER INTERRUPT
306
307 ============================================================================
308 */
309
310
311 /*
312 ================
313 =
314 = I_TimerISR
315 =
316 ================
317 */
318
319 int I_TimerISR (void)
320 {
321         ticcount++;
322         return 0;
323 }
324
325
326 /*
327 ============================================================================
328
329                                                         MOUSE
330
331 ============================================================================
332 */
333
334
335 /*
336 ================
337 =
338 = StartupMouse
339 =
340 ================
341 */
342
343 void I_StartupCyberMan(void);
344
345 void I_StartupMouse (void)
346 {
347         I_StartupCyberMan();
348 }
349
350
351 /*
352 ================
353 =
354 = I_ReadMouse
355 =
356 ================
357 */
358
359 void I_ReadMouse (void)
360 {
361 }
362
363
364
365 //==========================================================================
366 //
367 //
368 // I_StartupReadKeys
369 //
370 //
371 //==========================================================================
372
373 void I_StartupReadKeys(void)
374 {
375    //if( KEY_ESCAPE pressed )
376    //    I_Quit ();
377 }
378
379
380 void checkGLContext()
381 {
382     GLXContext c = glXGetCurrentContext();
383     if( ! c || (c != _win->context()) )
384         printf( "Bad context!\n" );
385     else
386         printf( "context ok\n" );
387 }
388
389 }   // extern "C"
390
391
392 //---------------------------------------------------------------------------
393
394
395 HexenWindow::HexenWindow()
396         : X11Window( "HHEXEN", 0, 0, screenWidth, screenHeight )
397 {
398     int attrib[] = { GLX_RGBA,
399                      GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
400                      GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, None };
401
402     _ctx = 0;
403     _vinfo = 0;
404     _grabCursor = true;
405
406     _buttons = 0;
407     _prevX = _prevY = 0; 
408
409     if( glXQueryExtension( display(), 0, 0 ) == 0 )
410     {
411         fprintf( stderr, "GLX Extension not available!\n" );
412         return;
413     }
414
415     _vinfo = glXChooseVisual( display(), screen(), attrib );
416     if( ! _vinfo )
417     {
418         fprintf( stderr, "Couldn't get an RGB, double-buffered visual!\n" );
419         return;
420     }
421
422     _ctx = glXCreateContext( display(), _vinfo, NULL, True );
423     glXMakeCurrent( display(), window(), _ctx );
424
425     setTitle( "HHexen v1.3" );
426     setSizeHints( screenWidth,screenHeight,screenWidth,screenHeight );
427     setIconName( "HHEXEN" );
428 }
429
430
431 HexenWindow::~HexenWindow()
432 {
433     if( _ctx )
434     {
435         glXDestroyContext( display(), _ctx );
436     }
437 }
438
439
440 void HexenWindow::deleteEvent( XEvent* )
441 {
442     I_Quit();
443 }
444
445 void HexenWindow::configureEvent( XConfigureEvent* e )
446 {
447     screenWidth  = width();
448     screenHeight = height();
449 }
450
451 void HexenWindow::unknownEvent( XEvent* e )
452 {
453 }
454
455
456 void HexenWindow::buttonDown( XButtonEvent* e )
457 {
458     if( ! _grabCursor )
459         grabPointer();
460
461     switch( e->button )
462     {
463         case Button1: _buttons |= MOUSEB1 ; break;
464         case Button2: _buttons |= MOUSEB2 ; break;
465         case Button3: _buttons |= MOUSEB3 ; break;
466         default:
467             return;
468     }
469     postMouseEvent( 0, 0 );
470 }
471
472
473 void HexenWindow::buttonUp( XButtonEvent* e )
474 {
475     switch( e->button )
476     {
477         case Button1: _buttons &= ~MOUSEB1 ; break;
478         case Button2: _buttons &= ~MOUSEB2 ; break;
479         case Button3: _buttons &= ~MOUSEB3 ; break;
480         default:
481             return;
482     }
483     postMouseEvent( 0, 0 );
484 }
485
486
487 void HexenWindow::motionEvent( XMotionEvent* e )
488 {
489
490    int dx,dy;
491
492    if (e->x == width()/2 && e->y == height()/2)
493    {
494         _prevX = e->x;
495         _prevY = e->y;
496         return;
497    }
498    dx = (e->x - _prevX);
499    _prevX = e->x;
500    dy = (e->y - _prevY);
501    _prevY = e->y;
502
503    if( dx || dy )
504    {
505       postMouseEvent( dx, dy );
506
507       if( _grabCursor )
508       {
509           if( (e->x < 30) || (e->x > (screenWidth-30)) ||
510               (e->y < 30) || (e->y > (screenHeight-30)) )
511           {
512               XWarpPointer( display(), None, window(), 0, 0, 0, 0,
513                             screenWidth / 2, screenHeight / 2 );
514               _prevX = e->x; _prevY = e->y;
515           }
516       }
517    }
518 }
519
520 void HexenWindow::keyDown( XKeyEvent* e )
521 {
522     KeySym key = keysym( e );
523
524     //TODO: filter key repeat.
525
526     if( e->state & Mod1Mask )   // Control key defaults to attack.
527     {
528         if( key == XK_s )
529         {
530             OGL_GrabScreen();
531         }
532         else if( key == XK_g )
533         {
534             if( _grabCursor )
535             {
536                 ungrabPointer();
537             }
538             else
539             {
540                 grabPointer();
541             }
542         }   
543     }
544     else
545     {
546         postKey( ev_keydown, key );
547     }
548 }
549
550
551 void HexenWindow::keyUp( XKeyEvent* e )
552 {
553     postKey( ev_keyup, keysym( e ) );
554 }
555
556
557 void HexenWindow::exposeEvent( XExposeEvent* )
558 {
559     UpdateState |= I_FULLSCRN;
560 }
561
562
563 void HexenWindow::postKey( evtype_t type, KeySym key )
564 {
565         event_t ev;
566
567     ev.type = type;
568
569     switch( key )
570     {
571         case XK_Up:     ev.data1 = KEY_UPARROW;    break;
572         case XK_Down:   ev.data1 = KEY_DOWNARROW;  break;
573         case XK_Left:   ev.data1 = KEY_LEFTARROW;  break;
574         case XK_Right:  ev.data1 = KEY_RIGHTARROW; break;
575
576         case XK_Escape: ev.data1 = KEY_ESCAPE;     break;
577         case XK_Return: ev.data1 = KEY_ENTER;      break;
578         case XK_F1:     ev.data1 = KEY_F1;         break;
579         case XK_F2:     ev.data1 = KEY_F2;         break;
580         case XK_F3:     ev.data1 = KEY_F3;         break;
581         case XK_F4:     ev.data1 = KEY_F4;         break;
582         case XK_F5:     ev.data1 = KEY_F5;         break;
583         case XK_F6:     ev.data1 = KEY_F6;         break;
584         case XK_F7:     ev.data1 = KEY_F7;         break;
585         case XK_F8:     ev.data1 = KEY_F8;         break;
586         case XK_F9:     ev.data1 = KEY_F9;         break;
587         case XK_F10:    ev.data1 = KEY_F10;        break;
588         case XK_F11:    ev.data1 = KEY_F11;        break;
589         case XK_F12:    ev.data1 = KEY_F12;        break;
590
591         case XK_Insert:    ev.data1 = KEY_INS;     break;
592         case XK_Delete:    ev.data1 = KEY_DEL;     break;
593         case XK_Page_Up:   ev.data1 = KEY_PGUP;    break;
594         case XK_Page_Down: ev.data1 = KEY_PGDN;    break;
595         case XK_Home:      ev.data1 = KEY_HOME;    break;
596         case XK_End:       ev.data1 = KEY_END;     break;
597
598         case XK_Tab:       ev.data1 = KEY_TAB;     break;
599
600         case XK_BackSpace: ev.data1 = KEY_BACKSPACE;  break;
601
602         case XK_Pause:     ev.data1 = KEY_PAUSE;      break;
603
604         case XK_equal:     ev.data1 = KEY_EQUALS;     break;
605
606         case XK_KP_Subtract:
607         case XK_minus:     ev.data1 = KEY_MINUS;      break;
608
609         case XK_Shift_L:
610         case XK_Shift_R:   ev.data1 = KEY_RSHIFT;     break;
611
612         case XK_Control_L:
613         case XK_Control_R: ev.data1 = KEY_RCTRL;      break;
614
615         case XK_Alt_L:
616         case XK_Alt_R:
617         case XK_Meta_L:
618         case XK_Meta_R:    ev.data1 = KEY_RALT;       break;
619
620         default:
621             ev.data1 = key;
622             break;
623     }
624
625     H2_PostEvent( &ev );
626 }
627
628
629 void HexenWindow::postMouseEvent( int dx, int dy )
630 {
631         event_t ev;
632
633         ev.type  = ev_mouse;
634         ev.data1 = _buttons;
635         ev.data2 =  (short) dx << 2;
636         ev.data3 = -(short) dy << 2;
637
638         H2_PostEvent( &ev );
639 }
640
641
642 // Returns zero if a unique file name could not be found.
643 static int makeUniqueFilename( char* filename )
644 {
645     int i;
646  
647     for( i = 0; i < 100; i++ )
648     {
649         sprintf( filename, "hexen%02d.ppm", i );
650  
651         if( access( filename, F_OK ) == -1 )
652         {
653             // It does not exist.
654             return 1;
655         }
656     }
657  
658     return 0;
659 }
660
661
662 //--------------------------------------------------------------------------
663 //
664 // Copies the current contents of the frame buffer and returns a pointer
665 // to data containing 24-bit RGB triplets.
666 //
667 //--------------------------------------------------------------------------
668
669 void OGL_GrabScreen()
670 {
671     FILE* fp;
672     char filename[ 20 ];
673         unsigned char* buffer = new unsigned char[ screenWidth * screenHeight * 3 ];
674
675     if( buffer && makeUniqueFilename( filename ) ) 
676     {
677         glReadPixels( 0, 0, screenWidth, screenHeight, GL_RGB, 
678                       GL_UNSIGNED_BYTE, buffer );
679
680         fp = fopen( filename, "w" );
681         if( fp )
682         {
683             unsigned char* rgb = buffer + (screenWidth * screenHeight * 3);
684
685             fprintf( fp, "P6\n%d %d\n255\n", screenWidth, screenHeight );
686
687             while( rgb > buffer )
688             {
689                 rgb -= 3 * screenWidth;
690                 fwrite( rgb, 1, 3 * screenWidth, fp );
691             }
692
693             fclose( fp );
694         }
695     }
696     
697     delete buffer;
698 }
699
700 //EOF