1 //**************************************************************************
5 //**************************************************************************
13 #include <X11/keysym.h>
14 #include "x11window.h"
23 Nifty palette cacheing idea taken from Linux Heretic.
25 The Hexen palette pointer is used as a palette id, i.e. a palette
26 with another pointer value is assumed to be a different
27 palette, and, more important, if the pointer is equal,
28 the palette is assumed to be the same...
32 #define MAX_PAL_CACHE 5
34 #define MOUSE_JUMP_AT 10
38 PaletteCache() : id( 0 ), used( 0 ) {}
40 void free( Display* dis, Colormap cmap )
44 XFreeColors( dis, cmap, pixel, 256, 0 );
50 unsigned char* id; // contains pointer / id of palette.
52 unsigned long pixel[ 256 ]; // xpixel lookup table.
56 class HexenWindow : public X11Window
64 void setPalette( byte* );
66 void blit( unsigned char* buffer, int x, int y, int w, int h );
70 XGrabPointer( display(), window(), True,
71 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
72 GrabModeAsync, GrabModeAsync, window(), None, CurrentTime );
79 XUngrabPointer( display(), CurrentTime );
86 virtual void unknownEvent( XEvent* );
87 virtual void configureEvent( XConfigureEvent* );
88 virtual void deleteEvent( XEvent* );
89 virtual void buttonDown( XButtonEvent* );
90 virtual void buttonUp( XButtonEvent* );
91 virtual void motionEvent( XMotionEvent* );
92 virtual void keyDown( XKeyEvent* );
93 virtual void keyUp( XKeyEvent* );
94 virtual void exposeEvent( XExposeEvent* );
99 void resizeFramebuffer();
100 bool getVisualInfo( XVisualInfo* );
101 void postKey( evtype_t type, KeySym key );
102 void postMouseEvent( int dx, int dy );
108 PaletteCache _palc[ MAX_PAL_CACHE ];
109 PaletteCache* _pcurrent;
133 int DisplayTicker = 0;
137 extern boolean novideo; // if true, stay in text mode for debugging
140 //==================================================
146 //==================================================
148 #define KEY_TAB 9 // From am_map.h
152 #define KEY_PGUP 0x49
153 #define KEY_PGDN 0x51
154 #define KEY_HOME 0x47
159 ============================================================================
163 ============================================================================
166 //--------------------------------------------------------------------------
170 //--------------------------------------------------------------------------
172 void I_WaitVBL(int vbls)
184 //--------------------------------------------------------------------------
188 // Palette source must use 8 bit RGB elements.
190 //--------------------------------------------------------------------------
192 void I_SetPalette( byte *palette )
199 _win->setPalette( palette );
203 ============================================================================
207 ============================================================================
220 extern int screenblocks;
230 // blit screen to video
235 // Why is it drawing to pcscreen under some conditions?
236 if(screenblocks > 9 || UpdateState&(I_FULLSCRN|I_MESSAGES))
238 dest = (byte *)screen;
242 dest = (byte *)pcscreen;
245 dest = (byte *)screen;
248 tics = ticcount-lasttic;
254 for(i = 0; i < tics; i++)
259 for(i = tics; i < 20; i++)
266 if(UpdateState == I_NOUPDATE)
270 if(UpdateState&I_FULLSCRN)
272 UpdateState = I_NOUPDATE; // clear out all draw types
274 _win->blit( screen, 0, 0, SCREENWIDTH, SCREENHEIGHT );
276 if(UpdateState&I_FULLVIEW)
278 if(UpdateState&I_MESSAGES && screenblocks > 7)
280 UpdateState &= ~(I_FULLVIEW|I_MESSAGES);
282 _win->blit( screen, 0, 0, SCREENWIDTH, viewwindowy+viewheight );
286 UpdateState &= ~I_FULLVIEW;
288 _win->blit( screen, viewwindowx, viewwindowy, viewwidth,
292 if(UpdateState&I_STATBAR)
294 UpdateState &= ~I_STATBAR;
296 _win->blit( screen, 0, SCREENHEIGHT-SBARHEIGHT,
297 SCREENWIDTH, SBARHEIGHT );
299 if(UpdateState&I_MESSAGES)
301 UpdateState &= ~I_MESSAGES;
303 _win->blit( screen, 0, 0, SCREENWIDTH, 28 );
307 //--------------------------------------------------------------------------
309 // PROC I_InitGraphics
311 //--------------------------------------------------------------------------
313 void I_InitGraphics(void)
320 _win = new HexenWindow();
328 I_SetPalette( (byte*) W_CacheLumpName("PLAYPAL", PU_CACHE) );
329 _win->grabPointer ();
332 //--------------------------------------------------------------------------
334 // PROC I_ShutdownGraphics
336 //--------------------------------------------------------------------------
338 void I_ShutdownGraphics(void)
342 //--------------------------------------------------------------------------
346 // Reads the screen currently displayed into a linear buffer.
348 //--------------------------------------------------------------------------
351 void I_ReadScreen(byte *scr)
353 memcpy(scr, screen, SCREENWIDTH*SCREENHEIGHT);
357 //===========================================================================
360 void I_StartTic (void)
362 // Handle keyboard & mouse events.
364 while( _win->eventsPending() )
365 _win->handleNextEvent();
372 ============================================================================
376 ============================================================================
388 int I_TimerISR (void)
396 ============================================================================
400 ============================================================================
412 void I_StartupCyberMan(void);
414 void I_StartupMouse (void)
428 void I_ReadMouse (void)
434 //==========================================================================
440 //==========================================================================
442 void I_StartupReadKeys(void)
444 //if( KEY_ESCAPE pressed )
452 //---------------------------------------------------------------------------
455 HexenWindow::HexenWindow()
456 : X11Window( "HEXEN", 0, 0, SCREENWIDTH*2, SCREENHEIGHT*2 )
472 setTitle( "HHexen v1.3" );
473 setSizeHints( SCREENWIDTH, SCREENHEIGHT, SCREENWIDTH*2, SCREENHEIGHT*2 );
474 setIconName( "HEXEN" );
476 if( getVisualInfo( &_vinfo ) )
479 // printf( "Visual: depth %d, mask %04x %04x %04x\n", _vinfo.depth,
480 // _vinfo.red_mask, _vinfo.green_mask, _vinfo.blue_mask );
482 if( _vinfo.depth == 8 )
484 _colormap = XCreateColormap( display(), window(), _vinfo.visual,
486 XSetWindowColormap( display(), window(), _colormap );
490 _colormap = DefaultColormapOfScreen(
491 ScreenOfDisplay( display(), screen() ) );
495 gcv.graphics_exposures = False;
496 _gc = XCreateGC( display(), window(), GCGraphicsExposures, &gcv );
498 fprintf( stderr, "XCreateGC failed!\n" );
500 _shmEventType = ShmImage::query( display() );
503 printf( "Using X11 MITSHM extension\n" );
504 _usingShm = true; // May provide a user disable for Shm later.
511 fprintf( stderr, "XMatchVisualInfo failed!\n" );
516 HexenWindow::~HexenWindow()
518 if( _vinfo.depth == 8 )
521 XFreeColormap( display(), _colormap );
526 for( i = 0; i < MAX_PAL_CACHE; i++ )
527 _palc[ i ].free( display(), _colormap );
536 delete _ximage->data;
537 XDestroyImage( _ximage );
542 void HexenWindow::setPalette( byte* palette )
546 if( _vinfo.c_class == PseudoColor && _vinfo.depth == 8 )
548 XColor colors[ 256 ];
550 for( i = 0 ; i < 256 ; i++ )
552 colors[ i ].pixel = i;
553 colors[ i ].flags = DoRed | DoGreen | DoBlue;
555 c = gammatable[usegamma][*palette++];
556 colors[ i ].red = (c << 8) + c;
558 c = gammatable[usegamma][*palette++];
559 colors[ i ].green = (c << 8) + c;
561 c = gammatable[usegamma][*palette++];
562 colors[ i ].blue = (c << 8) + c;
565 XStoreColors( display(), _colormap, colors, 256 );
571 // It looks like we could get away with caching as little as
572 // three palettes to avoid most re-allocations.
574 // Must update the entire screen when palette changes in truecolor mode.
575 UpdateState |= I_FULLSCRN;
578 if( usegamma != _cacheGamma )
580 // The gamma has changed so the entire cache must be thrown out.
581 for( i = 0; i < MAX_PAL_CACHE; i++ )
582 _palc[ i ].free( display(), _colormap );
584 _cacheGamma = usegamma;
585 //printf( "palette gamma\n" );
588 // Search for palette in cache.
589 for( i = 0; i < MAX_PAL_CACHE; i++ )
591 if( _palc[ i ].id == palette )
594 _pcurrent = &_palc[ i ];
595 _pcurrent->used = ++_cacheAge;
597 //printf( "palette %d %p - cache\n", i, palette );
604 // Search for unused cache. Otherwise use the oldest cache.
605 _pcurrent = &_palc[ 0 ];
608 for( i = 0; i < MAX_PAL_CACHE; i++ )
610 if( ! _palc[ i ].id )
613 _pcurrent = &_palc[ i ];
616 if( _palc[ i ].used < _pcurrent->used )
617 _pcurrent = &_palc[ i ];
621 //printf( "palette %d %p - new\n", i, palette );
623 _pcurrent->free( display(), _colormap );
624 _pcurrent->used = ++_cacheAge;
625 _pcurrent->id = palette;
627 for( i = 0 ; i < 256 ; i++ )
630 color.flags = DoRed | DoGreen | DoBlue;
632 c = gammatable[usegamma][*palette++];
633 color.red = (c << 8);
635 c = gammatable[usegamma][*palette++];
636 color.green = (c << 8);
638 c = gammatable[usegamma][*palette++];
639 color.blue = (c << 8);
641 if( ! XAllocColor( display(), _colormap, &color ) )
643 fprintf( stderr," XAllocColor failed\n" );
646 _pcurrent->pixel[ i ] = color.pixel;
652 // Predicate procedure used by waitOnShmPut()
656 PPArg( Window w, int type ) : window( w ), shmCompletionType( type ) {}
659 int shmCompletionType;
662 static Bool _shmPredicateProc( Display* dis, XEvent* event, XPointer arg )
664 if( (event->xany.window == ((PPArg*)arg)->window) &&
665 (event->type == ((PPArg*)arg)->shmCompletionType) )
672 void HexenWindow::waitForShmPut()
675 PPArg arg( window(), _shmEventType );
676 XIfEvent( display(), &event, _shmPredicateProc, (XPointer) &arg );
680 typedef unsigned char pixel8;
681 typedef unsigned short pixel16;
682 typedef unsigned long pixel32;
684 static void blit_8_8( pixel8* source, XImage* img,
685 int x, int y, int w, int h )
687 register pixel8* begin;
689 begin = (pixel8*) img->data;
690 begin += x + (y * img->bytes_per_line);
693 memcpy( begin, source, w );
694 begin += img->bytes_per_line;
700 static void blit_double_8_8( pixel8* source, XImage* img,
701 int x, int y, int w, int h )
706 register pixel8* dst;
707 register pixel8* src;
709 begin = (pixel8*) img->data;
710 begin += (x * 2) + (y * img->bytes_per_line * 2);
723 memcpy( begin + img->bytes_per_line, begin, w * 2 );
725 begin += img->bytes_per_line * 2;
730 static void blit_8_16( pixel8* source, XImage* img,
731 int x, int y, int w, int h,
732 unsigned long* pixel )
736 register pixel16* dst;
737 register pixel8* src;
739 begin = (pixel16*) img->data;
740 begin += x + (y * img->bytes_per_line / 2);
747 *dst++ = pixel[ *src++ ];
748 begin += img->bytes_per_line / 2;
753 static void blit_double_8_16( pixel8* source, XImage* img,
754 int x, int y, int w, int h,
755 unsigned long* pixel )
760 register pixel16* dst;
761 register pixel8* src;
763 begin = (pixel16*) img->data;
764 begin += (x * 2) + (y * img->bytes_per_line);
777 dst = begin + (img->bytes_per_line / 2);
778 memcpy( dst, begin, w * 4 );
780 begin += img->bytes_per_line;
785 static void blit_8_32( pixel8* source, XImage* img,
786 int x, int y, int w, int h,
787 unsigned long* pixel )
791 register pixel32* dst;
792 register unsigned char* src;
794 begin = (pixel32*) img->data;
795 begin += x + (y * img->bytes_per_line / 4);
802 *dst++ = pixel[ *src++ ];
803 begin += img->bytes_per_line / 4;
808 static void blit_double_8_32( pixel8* source, XImage* img,
809 int x, int y, int w, int h,
810 unsigned long* pixel )
815 register pixel32* dst;
816 register pixel8* src;
818 begin = (pixel32*) img->data;
819 begin += (x * 2) + (y * img->bytes_per_line / 2);
832 dst = begin + (img->bytes_per_line / 4);
833 memcpy( dst, begin, w * 8 );
835 begin += img->bytes_per_line / 2;
840 void HexenWindow::blit( unsigned char* buffer, int x, int y, int w, int h )
842 buffer += x + (y * w);
844 if( (width() >= SCREENWIDTH*2) && (height() >= SCREENHEIGHT*2) )
846 if( _vinfo.depth == 8 )
848 blit_double_8_8( buffer, _ximage, x, y, w, h );
850 else if( _vinfo.depth <= 16 )
852 blit_double_8_16( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
856 blit_double_8_32( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
865 if( _vinfo.depth == 8 )
867 blit_8_8( buffer, _ximage, x, y, w, h );
869 else if( _vinfo.depth <= 16 )
871 blit_8_16( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
875 blit_8_32( buffer, _ximage, x, y, w, h, _pcurrent->pixel );
881 XShmPutImage( display(), window(), _gc, _ximage, x, y, x, y,
887 XPutImage( display(), window(), _gc, _ximage, x, y, x, y, w, h );
893 void HexenWindow::resizeFramebuffer()
895 if(_ximage && (_ximage->width == width()) && (_ximage->height == height()))
903 _simage = new ShmImage( display(), width(), height(), &_vinfo );
904 _ximage = _simage->image();
910 delete _ximage->data;
911 XDestroyImage( _ximage );
913 _ximage = XCreateImage( display(), _vinfo.visual, _vinfo.depth,
916 (_vinfo.depth == 8) ? 8 : 32, 0 );
917 _ximage->data = new char[ _ximage->bytes_per_line * _ximage->height ];
920 UpdateState |= I_FULLSCRN;
924 bool HexenWindow::getVisualInfo( XVisualInfo* vi )
926 if( XMatchVisualInfo( display(), screen(), 8, PseudoColor, vi ) )
929 if( XMatchVisualInfo( display(), screen(), 16, TrueColor, vi ) )
931 if( XMatchVisualInfo( display(), screen(), 15, TrueColor, vi ) )
934 if( XMatchVisualInfo( display(), screen(), 32, TrueColor, vi ) )
936 if( XMatchVisualInfo( display(), screen(), 24, TrueColor, vi ) )
940 if( XMatchVisualInfo( display(), screen(), 8, GrayScale, vi ) )
948 void HexenWindow::deleteEvent( XEvent* )
953 void HexenWindow::configureEvent( XConfigureEvent* e )
956 //printf( "configure %d %d\n", width(), height() );
959 void HexenWindow::unknownEvent( XEvent* e )
961 //printf( "Unknown XEvent: %d\n", e->type );
965 void HexenWindow::buttonDown( XButtonEvent* e )
967 //printf( "buttonDown: %d %d,%d\n", e->button, e->x, e->y );
969 // if( ! _grabCursor )
974 case Button1: _buttons |= MOUSEB1 ; break;
975 case Button2: _buttons |= MOUSEB2 ; break;
976 case Button3: _buttons |= MOUSEB3 ; break;
980 postMouseEvent( 0, 0 );
984 void HexenWindow::buttonUp( XButtonEvent* e )
986 //printf( "buttonUp: %d %d,%d\n", e->button, e->x, e->y );
990 case Button1: _buttons &= ~MOUSEB1 ; break;
991 case Button2: _buttons &= ~MOUSEB2 ; break;
992 case Button3: _buttons &= ~MOUSEB3 ; break;
996 postMouseEvent( 0, 0 );
1000 void HexenWindow::motionEvent( XMotionEvent* e )
1005 if(e->x == width()/2 && e->y == height()/2)
1008 _prevY = height()/2;
1011 dx = (e->x - _prevX);
1013 dy = (e->y - _prevY);
1018 postMouseEvent( dx, dy );
1023 if( (e->x < MOUSE_JUMP_AT) || (e->x > (width() - MOUSE_JUMP_AT)) ||
1024 (e->y < MOUSE_JUMP_AT) || (e->y > (height() - MOUSE_JUMP_AT)) )
1026 XWarpPointer( display(), None, window(), 0, 0, 0, 0,
1027 width() / 2, height() / 2 );
1028 _prevX = width()/2; _prevY = height()/2;
1032 postMouseEvent( dx, dy);
1037 postMouseEvent( dx, dy);
1043 void HexenWindow::keyDown( XKeyEvent* e )
1045 KeySym key = keysym( e );
1047 //TODO: filter key repeat.
1049 //printf( "keyDown: %lx %x\n", e->time, key );
1051 if( e->state & Mod1Mask ) // Control key defaults to attack.
1055 if( width() == SCREENWIDTH )
1057 resize( SCREENWIDTH * 2, SCREENHEIGHT * 2 );
1061 resize( SCREENWIDTH, SCREENHEIGHT );
1064 else if( key == XK_g )
1077 else if( key == XK_Escape )
1084 postKey( ev_keydown, key );
1089 void HexenWindow::keyUp( XKeyEvent* e )
1091 //printf( "keyUp: %lx %x %x\n", e->time, e->state, e->keycode );
1093 postKey( ev_keyup, keysym( e ) );
1097 void HexenWindow::exposeEvent( XExposeEvent* )
1099 //printf( "expose\n" );
1100 UpdateState |= I_FULLSCRN;
1104 void HexenWindow::postKey( evtype_t type, KeySym key )
1112 case XK_Up: ev.data1 = KEY_UPARROW; break;
1113 case XK_Down: ev.data1 = KEY_DOWNARROW; break;
1114 case XK_Left: ev.data1 = KEY_LEFTARROW; break;
1115 case XK_Right: ev.data1 = KEY_RIGHTARROW; break;
1117 case XK_Escape: ev.data1 = KEY_ESCAPE; break;
1118 case XK_Return: ev.data1 = KEY_ENTER; break;
1119 case XK_F1: ev.data1 = KEY_F1; break;
1120 case XK_F2: ev.data1 = KEY_F2; break;
1121 case XK_F3: ev.data1 = KEY_F3; break;
1122 case XK_F4: ev.data1 = KEY_F4; break;
1123 case XK_F5: ev.data1 = KEY_F5; break;
1124 case XK_F6: ev.data1 = KEY_F6; break;
1125 case XK_F7: ev.data1 = KEY_F7; break;
1126 case XK_F8: ev.data1 = KEY_F8; break;
1127 case XK_F9: ev.data1 = KEY_F9; break;
1128 case XK_F10: ev.data1 = KEY_F10; break;
1129 case XK_F11: ev.data1 = KEY_F11; break;
1130 case XK_F12: ev.data1 = KEY_F12; break;
1132 case XK_Insert: ev.data1 = KEY_INS; break;
1133 case XK_Delete: ev.data1 = KEY_DEL; break;
1134 case XK_Page_Up: ev.data1 = KEY_PGUP; break;
1135 case XK_Page_Down: ev.data1 = KEY_PGDN; break;
1136 case XK_Home: ev.data1 = KEY_HOME; break;
1137 case XK_End: ev.data1 = KEY_END; break;
1139 case XK_Tab: ev.data1 = KEY_TAB; break;
1141 case XK_BackSpace: ev.data1 = KEY_BACKSPACE; break;
1143 case XK_Pause: ev.data1 = KEY_PAUSE; break;
1145 case XK_equal: ev.data1 = KEY_EQUALS; break;
1147 case XK_KP_Subtract:
1148 case XK_minus: ev.data1 = KEY_MINUS; break;
1151 case XK_Shift_R: ev.data1 = KEY_RSHIFT; break;
1154 case XK_Control_R: ev.data1 = KEY_RCTRL; break;
1159 case XK_Meta_R: ev.data1 = KEY_RALT; break;
1166 H2_PostEvent( &ev );
1170 void HexenWindow::postMouseEvent( int dx, int dy )
1175 ev.data1 = _buttons;
1176 ev.data2 = (short) dx << 2;
1177 ev.data3 = -(short) dy << 2;
1179 H2_PostEvent( &ev );