1 //**************************************************************************
5 //**************************************************************************
14 #include <X11/keysym.h>
15 #include "x11window.h"
24 Nifty palette cacheing idea taken from Linux Heretic.
26 The Hexen palette pointer is used as a palette id, i.e. a palette
27 with another pointer value is assumed to be a different
28 palette, and, more important, if the pointer is equal,
29 the palette is assumed to be the same...
33 #define MAX_PAL_CACHE 5
35 #define MOUSE_JUMP_AT 10
39 PaletteCache() : id( 0 ), used( 0 ) {}
41 void free( Display* dis, Colormap cmap )
45 XFreeColors( dis, cmap, pixel, 256, 0 );
51 unsigned char* id; // contains pointer / id of palette.
53 unsigned long pixel[ 256 ]; // xpixel lookup table.
57 class HexenWindow : public X11Window
65 void setPalette( byte* );
67 void blit( unsigned char* buffer, int x, int y, int w, int h );
71 XGrabPointer( display(), window(), True,
72 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
73 GrabModeAsync, GrabModeAsync, window(), None, CurrentTime );
80 XUngrabPointer( display(), CurrentTime );
87 virtual void unknownEvent( XEvent* );
88 virtual void configureEvent( XConfigureEvent* );
89 virtual void deleteEvent( XEvent* );
90 virtual void buttonDown( XButtonEvent* );
91 virtual void buttonUp( XButtonEvent* );
92 virtual void motionEvent( XMotionEvent* );
93 virtual void keyDown( XKeyEvent* );
94 virtual void keyUp( XKeyEvent* );
95 virtual void exposeEvent( XExposeEvent* );
100 void resizeFramebuffer();
101 bool getVisualInfo( XVisualInfo* );
102 void postKey( evtype_t type, KeySym key );
103 void postMouseEvent( int dx, int dy );
109 PaletteCache _palc[ MAX_PAL_CACHE ];
110 PaletteCache* _pcurrent;
134 int DisplayTicker = 0;
138 extern boolean novideo; // if true, stay in text mode for debugging
141 //==================================================
147 //==================================================
149 #define KEY_TAB 9 // From am_map.h
153 #define KEY_PGUP 0x49
154 #define KEY_PGDN 0x51
155 #define KEY_HOME 0x47
160 ============================================================================
164 ============================================================================
167 //--------------------------------------------------------------------------
171 //--------------------------------------------------------------------------
173 void I_WaitVBL(int vbls)
185 //--------------------------------------------------------------------------
189 // Palette source must use 8 bit RGB elements.
191 //--------------------------------------------------------------------------
193 void I_SetPalette( byte *palette )
200 _win->setPalette( palette );
204 ============================================================================
208 ============================================================================
221 extern int screenblocks;
231 // blit screen to video
236 // Why is it drawing to pcscreen under some conditions?
237 if(screenblocks > 9 || UpdateState&(I_FULLSCRN|I_MESSAGES))
239 dest = (byte *)screen;
243 dest = (byte *)pcscreen;
246 dest = (byte *)screen;
249 tics = ticcount-lasttic;
255 for(i = 0; i < tics; i++)
260 for(i = tics; i < 20; i++)
267 if(UpdateState == I_NOUPDATE)
271 if(UpdateState&I_FULLSCRN)
273 UpdateState = I_NOUPDATE; // clear out all draw types
275 _win->blit( screen, 0, 0, SCREENWIDTH, SCREENHEIGHT );
277 if(UpdateState&I_FULLVIEW)
279 if(UpdateState&I_MESSAGES && screenblocks > 7)
281 UpdateState &= ~(I_FULLVIEW|I_MESSAGES);
283 _win->blit( screen, 0, 0, SCREENWIDTH, viewwindowy+viewheight );
287 UpdateState &= ~I_FULLVIEW;
289 _win->blit( screen, viewwindowx, viewwindowy, viewwidth,
293 if(UpdateState&I_STATBAR)
295 UpdateState &= ~I_STATBAR;
297 _win->blit( screen, 0, SCREENHEIGHT-SBARHEIGHT,
298 SCREENWIDTH, SBARHEIGHT );
300 if(UpdateState&I_MESSAGES)
302 UpdateState &= ~I_MESSAGES;
304 _win->blit( screen, 0, 0, SCREENWIDTH, 28 );
308 //--------------------------------------------------------------------------
310 // PROC I_InitGraphics
312 //--------------------------------------------------------------------------
314 void I_InitGraphics(void)
321 _win = new HexenWindow();
329 I_SetPalette( (byte*) W_CacheLumpName("PLAYPAL", PU_CACHE) );
330 _win->grabPointer ();
333 //--------------------------------------------------------------------------
335 // PROC I_ShutdownGraphics
337 //--------------------------------------------------------------------------
339 void I_ShutdownGraphics(void)
343 //--------------------------------------------------------------------------
347 // Reads the screen currently displayed into a linear buffer.
349 //--------------------------------------------------------------------------
352 void I_ReadScreen(byte *scr)
354 memcpy(scr, screen, SCREENWIDTH*SCREENHEIGHT);
358 //===========================================================================
361 void I_StartTic (void)
363 // Handle keyboard & mouse events.
365 while( _win->eventsPending() )
366 _win->handleNextEvent();
373 ============================================================================
377 ============================================================================
389 int I_TimerISR (void)
397 ============================================================================
401 ============================================================================
413 void I_StartupCyberMan(void);
415 void I_StartupMouse (void)
429 void I_ReadMouse (void)
435 //==========================================================================
441 //==========================================================================
443 void I_StartupReadKeys(void)
445 //if( KEY_ESCAPE pressed )
453 //---------------------------------------------------------------------------
456 HexenWindow::HexenWindow()
457 : X11Window( "HEXEN", 0, 0, SCREENWIDTH*2, SCREENHEIGHT*2 )
473 setTitle( "HHexen v1.3" );
474 setSizeHints( SCREENWIDTH, SCREENHEIGHT, SCREENWIDTH*2, SCREENHEIGHT*2 );
475 setIconName( "HEXEN" );
477 if( getVisualInfo( &_vinfo ) )
480 // printf( "Visual: depth %d, mask %04x %04x %04x\n", _vinfo.depth,
481 // _vinfo.red_mask, _vinfo.green_mask, _vinfo.blue_mask );
483 if( _vinfo.depth == 8 )
485 _colormap = XCreateColormap( display(), window(), _vinfo.visual,
487 XSetWindowColormap( display(), window(), _colormap );
491 _colormap = DefaultColormapOfScreen(
492 ScreenOfDisplay( display(), screen() ) );
496 gcv.graphics_exposures = False;
497 _gc = XCreateGC( display(), window(), GCGraphicsExposures, &gcv );
499 fprintf( stderr, "XCreateGC failed!\n" );
501 _shmEventType = ShmImage::query( display() );
504 printf( "Using X11 MITSHM extension\n" );
505 _usingShm = true; // May provide a user disable for Shm later.
512 fprintf( stderr, "XMatchVisualInfo failed!\n" );
517 HexenWindow::~HexenWindow()
519 if( _vinfo.depth == 8 )
522 XFreeColormap( display(), _colormap );
527 for( i = 0; i < MAX_PAL_CACHE; i++ )
528 _palc[ i ].free( display(), _colormap );
537 delete _ximage->data;
538 XDestroyImage( _ximage );
543 void HexenWindow::setPalette( byte* palette )
547 if( _vinfo.c_class == PseudoColor && _vinfo.depth == 8 )
549 XColor colors[ 256 ];
551 for( i = 0 ; i < 256 ; i++ )
553 colors[ i ].pixel = i;
554 colors[ i ].flags = DoRed | DoGreen | DoBlue;
556 c = gammatable[usegamma][*palette++];
557 colors[ i ].red = (c << 8) + c;
559 c = gammatable[usegamma][*palette++];
560 colors[ i ].green = (c << 8) + c;
562 c = gammatable[usegamma][*palette++];
563 colors[ i ].blue = (c << 8) + c;
566 XStoreColors( display(), _colormap, colors, 256 );
572 // It looks like we could get away with caching as little as
573 // three palettes to avoid most re-allocations.
575 // Must update the entire screen when palette changes in truecolor mode.
576 UpdateState |= I_FULLSCRN;
579 if( usegamma != _cacheGamma )
581 // The gamma has changed so the entire cache must be thrown out.
582 for( i = 0; i < MAX_PAL_CACHE; i++ )
583 _palc[ i ].free( display(), _colormap );
585 _cacheGamma = usegamma;
586 //printf( "palette gamma\n" );
589 // Search for palette in cache.
590 for( i = 0; i < MAX_PAL_CACHE; i++ )
592 if( _palc[ i ].id == palette )
595 _pcurrent = &_palc[ i ];
596 _pcurrent->used = ++_cacheAge;
598 //printf( "palette %d %p - cache\n", i, palette );
605 // Search for unused cache. Otherwise use the oldest cache.
606 _pcurrent = &_palc[ 0 ];
609 for( i = 0; i < MAX_PAL_CACHE; i++ )
611 if( ! _palc[ i ].id )
614 _pcurrent = &_palc[ i ];
617 if( _palc[ i ].used < _pcurrent->used )
618 _pcurrent = &_palc[ i ];
622 //printf( "palette %d %p - new\n", i, palette );
624 _pcurrent->free( display(), _colormap );
625 _pcurrent->used = ++_cacheAge;
626 _pcurrent->id = palette;
628 for( i = 0 ; i < 256 ; i++ )
631 color.flags = DoRed | DoGreen | DoBlue;
633 c = gammatable[usegamma][*palette++];
634 color.red = (c << 8);
636 c = gammatable[usegamma][*palette++];
637 color.green = (c << 8);
639 c = gammatable[usegamma][*palette++];
640 color.blue = (c << 8);
642 if( ! XAllocColor( display(), _colormap, &color ) )
644 fprintf( stderr," XAllocColor failed\n" );
647 _pcurrent->pixel[ i ] = color.pixel;
653 // Predicate procedure used by waitOnShmPut()
657 PPArg( Window w, int type ) : window( w ), shmCompletionType( type ) {}
660 int shmCompletionType;
663 static Bool _shmPredicateProc( Display* dis, XEvent* event, XPointer arg )
665 if( (event->xany.window == ((PPArg*)arg)->window) &&
666 (event->type == ((PPArg*)arg)->shmCompletionType) )
673 void HexenWindow::waitForShmPut()
676 PPArg arg( window(), _shmEventType );
677 XIfEvent( display(), &event, _shmPredicateProc, (XPointer) &arg );
681 typedef unsigned char pixel8;
682 typedef unsigned short pixel16;
683 typedef unsigned long pixel32;
685 static void blit_8_8( pixel8* source, XImage* img,
686 int x, int y, int w, int h )
688 register pixel8* begin;
690 begin = (pixel8*) img->data;
691 begin += x + (y * img->bytes_per_line);
694 memcpy( begin, source, w );
695 begin += img->bytes_per_line;
701 static void blit_double_8_8( pixel8* source, XImage* img,
702 int x, int y, int w, int h )
707 register pixel8* dst;
708 register pixel8* src;
710 begin = (pixel8*) img->data;
711 begin += (x * 2) + (y * img->bytes_per_line * 2);
724 memcpy( begin + img->bytes_per_line, begin, w * 2 );
726 begin += img->bytes_per_line * 2;
731 static void blit_8_16( pixel8* source, XImage* img,
732 int x, int y, int w, int h,
733 unsigned long* pixel )
737 register pixel16* dst;
738 register pixel8* src;
740 begin = (pixel16*) img->data;
741 begin += x + (y * img->bytes_per_line / 2);
748 *dst++ = pixel[ *src++ ];
749 begin += img->bytes_per_line / 2;
754 static void blit_double_8_16( pixel8* source, XImage* img,
755 int x, int y, int w, int h,
756 unsigned long* pixel )
761 register pixel16* dst;
762 register pixel8* src;
764 begin = (pixel16*) img->data;
765 begin += (x * 2) + (y * img->bytes_per_line);
778 dst = begin + (img->bytes_per_line / 2);
779 memcpy( dst, begin, w * 4 );
781 begin += img->bytes_per_line;
786 static void blit_8_32( pixel8* source, XImage* img,
787 int x, int y, int w, int h,
788 unsigned long* pixel )
792 register pixel32* dst;
793 register unsigned char* src;
795 begin = (pixel32*) img->data;
796 begin += x + (y * img->bytes_per_line / 4);
803 *dst++ = pixel[ *src++ ];
804 begin += img->bytes_per_line / 4;
809 static void blit_double_8_32( pixel8* source, XImage* img,
810 int x, int y, int w, int h,
811 unsigned long* pixel )
816 register pixel32* dst;
817 register pixel8* src;
819 begin = (pixel32*) img->data;
820 begin += (x * 2) + (y * img->bytes_per_line / 2);
833 dst = begin + (img->bytes_per_line / 4);
834 memcpy( dst, begin, w * 8 );
836 begin += img->bytes_per_line / 2;
841 void HexenWindow::blit( unsigned char* buffer, int x, int y, int w, int h )
843 buffer += x + (y * w);
845 if( (width() >= SCREENWIDTH*2) && (height() >= SCREENHEIGHT*2) )
847 if( _vinfo.depth == 8 )
849 blit_double_8_8( buffer, _ximage, x, y, w, h );
851 else if( _vinfo.depth <= 16 )
853 blit_double_8_16( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
857 blit_double_8_32( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
866 if( _vinfo.depth == 8 )
868 blit_8_8( buffer, _ximage, x, y, w, h );
870 else if( _vinfo.depth <= 16 )
872 blit_8_16( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
876 blit_8_32( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
882 XShmPutImage( display(), window(), _gc, _ximage, x, y, x, y,
888 XPutImage( display(), window(), _gc, _ximage, x, y, x, y, w, h );
894 void HexenWindow::resizeFramebuffer()
896 if(_ximage && (_ximage->width == width()) && (_ximage->height == height()))
904 _simage = new ShmImage( display(), width(), height(), &_vinfo );
905 _ximage = _simage->image();
911 delete _ximage->data;
912 XDestroyImage( _ximage );
914 _ximage = XCreateImage( display(), _vinfo.visual, _vinfo.depth,
917 (_vinfo.depth == 8) ? 8 : 32, 0 );
918 _ximage->data = new char[ _ximage->bytes_per_line * _ximage->height ];
921 UpdateState |= I_FULLSCRN;
925 bool HexenWindow::getVisualInfo( XVisualInfo* vi )
927 if( XMatchVisualInfo( display(), screen(), 8, PseudoColor, vi ) )
930 if( XMatchVisualInfo( display(), screen(), 16, TrueColor, vi ) )
932 if( XMatchVisualInfo( display(), screen(), 15, TrueColor, vi ) )
935 if( XMatchVisualInfo( display(), screen(), 32, TrueColor, vi ) )
937 if( XMatchVisualInfo( display(), screen(), 24, TrueColor, vi ) )
941 if( XMatchVisualInfo( display(), screen(), 8, GrayScale, vi ) )
949 void HexenWindow::deleteEvent( XEvent* )
954 void HexenWindow::configureEvent( XConfigureEvent* e )
957 //printf( "configure %d %d\n", width(), height() );
960 void HexenWindow::unknownEvent( XEvent* e )
962 //printf( "Unknown XEvent: %d\n", e->type );
966 void HexenWindow::buttonDown( XButtonEvent* e )
968 //printf( "buttonDown: %d %d,%d\n", e->button, e->x, e->y );
970 // if( ! _grabCursor )
975 case Button1: _buttons |= MOUSEB1 ; break;
976 case Button2: _buttons |= MOUSEB2 ; break;
977 case Button3: _buttons |= MOUSEB3 ; break;
981 postMouseEvent( 0, 0 );
985 void HexenWindow::buttonUp( XButtonEvent* e )
987 //printf( "buttonUp: %d %d,%d\n", e->button, e->x, e->y );
991 case Button1: _buttons &= ~MOUSEB1 ; break;
992 case Button2: _buttons &= ~MOUSEB2 ; break;
993 case Button3: _buttons &= ~MOUSEB3 ; break;
997 postMouseEvent( 0, 0 );
1001 void HexenWindow::motionEvent( XMotionEvent* e )
1006 if(e->x == width()/2 && e->y == height()/2)
1009 _prevY = height()/2;
1012 dx = (e->x - _prevX);
1014 dy = (e->y - _prevY);
1019 postMouseEvent( dx, dy );
1024 if( (e->x < MOUSE_JUMP_AT) || (e->x > (width() - MOUSE_JUMP_AT)) ||
1025 (e->y < MOUSE_JUMP_AT) || (e->y > (height() - MOUSE_JUMP_AT)) )
1027 XWarpPointer( display(), None, window(), 0, 0, 0, 0,
1028 width() / 2, height() / 2 );
1029 _prevX = width()/2; _prevY = height()/2;
1033 postMouseEvent( dx, dy);
1038 postMouseEvent( dx, dy);
1044 void HexenWindow::keyDown( XKeyEvent* e )
1046 KeySym key = keysym( e );
1048 //TODO: filter key repeat.
1050 //printf( "keyDown: %lx %x\n", e->time, key );
1052 if( e->state & Mod1Mask ) // Control key defaults to attack.
1056 if( width() == SCREENWIDTH )
1058 resize( SCREENWIDTH * 2, SCREENHEIGHT * 2 );
1062 resize( SCREENWIDTH, SCREENHEIGHT );
1065 else if( key == XK_g )
1078 else if( key == XK_Escape )
1085 postKey( ev_keydown, key );
1090 void HexenWindow::keyUp( XKeyEvent* e )
1092 //printf( "keyUp: %lx %x %x\n", e->time, e->state, e->keycode );
1094 postKey( ev_keyup, keysym( e ) );
1098 void HexenWindow::exposeEvent( XExposeEvent* )
1100 //printf( "expose\n" );
1101 UpdateState |= I_FULLSCRN;
1105 void HexenWindow::postKey( evtype_t type, KeySym key )
1113 case XK_Up: ev.data1 = KEY_UPARROW; break;
1114 case XK_Down: ev.data1 = KEY_DOWNARROW; break;
1115 case XK_Left: ev.data1 = KEY_LEFTARROW; break;
1116 case XK_Right: ev.data1 = KEY_RIGHTARROW; break;
1118 case XK_Escape: ev.data1 = KEY_ESCAPE; break;
1119 case XK_Return: ev.data1 = KEY_ENTER; break;
1120 case XK_F1: ev.data1 = KEY_F1; break;
1121 case XK_F2: ev.data1 = KEY_F2; break;
1122 case XK_F3: ev.data1 = KEY_F3; break;
1123 case XK_F4: ev.data1 = KEY_F4; break;
1124 case XK_F5: ev.data1 = KEY_F5; break;
1125 case XK_F6: ev.data1 = KEY_F6; break;
1126 case XK_F7: ev.data1 = KEY_F7; break;
1127 case XK_F8: ev.data1 = KEY_F8; break;
1128 case XK_F9: ev.data1 = KEY_F9; break;
1129 case XK_F10: ev.data1 = KEY_F10; break;
1130 case XK_F11: ev.data1 = KEY_F11; break;
1131 case XK_F12: ev.data1 = KEY_F12; break;
1133 case XK_Insert: ev.data1 = KEY_INS; break;
1134 case XK_Delete: ev.data1 = KEY_DEL; break;
1135 case XK_Page_Up: ev.data1 = KEY_PGUP; break;
1136 case XK_Page_Down: ev.data1 = KEY_PGDN; break;
1137 case XK_Home: ev.data1 = KEY_HOME; break;
1138 case XK_End: ev.data1 = KEY_END; break;
1140 case XK_Tab: ev.data1 = KEY_TAB; break;
1142 case XK_BackSpace: ev.data1 = KEY_BACKSPACE; break;
1144 case XK_Pause: ev.data1 = KEY_PAUSE; break;
1146 case XK_equal: ev.data1 = KEY_EQUALS; break;
1148 case XK_KP_Subtract:
1149 case XK_minus: ev.data1 = KEY_MINUS; break;
1152 case XK_Shift_R: ev.data1 = KEY_RSHIFT; break;
1155 case XK_Control_R: ev.data1 = KEY_RCTRL; break;
1160 case XK_Meta_R: ev.data1 = KEY_RALT; break;
1167 H2_PostEvent( &ev );
1171 void HexenWindow::postMouseEvent( int dx, int dy )
1176 ev.data1 = _buttons;
1177 ev.data2 = (short) dx << 2;
1178 ev.data3 = -(short) dy << 2;
1180 H2_PostEvent( &ev );