Unit m_SDLCRT; {$I M_OPS.PAS} (* Goals: - Cross platform input and output capable of 80x25, 80x50, 132x50 - Input events remapped as compatible with current code base - Full screen mode in Windows, OSX, and Linux if possible - Direct access (read only) to a virtual screen buffer something like: TSDLScreenBuffer = Record Line : Array[1..132] of Record Ch : Char; Attr : Byte; End; - Current X location - Current Y location - Current Text Attribute - Window function to set scroll points - GotoXY - Clear Screen (window based) - Clear to end of line (with current attribute) - Set Window title - Hide screen - Show Buffer - KeyPressed : Boolean - KeyWait (Timeout in seconds or MS) : Boolean - ReadKey (from buffer, or waits infinity if nothing) - Ability to wait for input event based on handle for WaitForMultipleObj ? - Ability to play a .WAV/MP3/MIDI file or at least some sound? - How to handle shutdown and resize window events? Lock resize? - How to handle minimize? Minimize to tray in Windows? *) Interface Uses SDL, SDL_TTF; Const keyENTER = #13; keyESCAPE = #27; keyHOME = #71; keyUP = #72; keyPGUP = #73; keyLEFT = #75; keyRIGHT = #77; keyEND = #79; keyDOWN = #80; keyPGDN = #81; keyINSERT = #82; keyDELETE = #83; AppInputSize = 128; SDLFontSize : Byte = 24; SDLFontSpace : Byte = 0; SDLAppWindowX : Word = 800; SDLAppWindowY : Word = 600; Type TSDLScreenMode = (mode_80x25, mode_80x50, mode_132x50); TSDLKeyMap = Record SDL : Word; Key : String[2]; Shift : String[2]; Alt : String[2]; Ctrl : String[2]; End; TSDLConsole = Class InputEvent : pSDL_EVENT; Screen : pSDL_SURFACE; Font : pTTF_Font; InputBuffer : Array[1..AppInputSize] of Char; InputPos : Integer; InputSize : Integer; CursorX : Byte; CursorY : Byte; TextAttr : Byte; // INIT STUFF Constructor Create (InitMode: TSDLScreenMode); Destructor Destroy; Override; // INTERNAL STUFF Procedure PushInput (Ch: Char); Procedure PushExt (Ch: Char); Procedure PushStr (Str: String); Procedure ProcessEvent; // Function GetDosForeground (Color: Byte) : TSDL_Color; // Function GetDosBackground (Color: Byte) : TSDL_Color; // FUNCTIONAL Function KeyPressed : Boolean; Function ReadKey : Char; Procedure Delay (MS: LongInt); Procedure SetTitle (Title: String); Procedure ShowBuffer; //NONSENSE Procedure TestStuff; End; Implementation // SDL fails hard with keyboard handling. I think we need to use // a lookup table that can be externalized into a data file so different // countries can load their specific keyboard set. // // I just assumed things like this would NOT be a problem in SDL. Pretty // disappointing actually. So below is the US mapping as I get time to // work on it. Const SDLKeyMapSize = 3; SDLKeyMapUS : Array[1..SDLKeyMapSize] of TSDLKeyMap = ( (SDL:SDLK_1; Key:'1'; Shift:'!'; Alt:'1'; CTRL:'1'), (SDL:SDLK_2; Key:'2'; Shift:'@'; Alt:'2'; CTRL:'2'), (SDL:SDLK_SLASH; Key:'/'; Shift:'?'; Alt:'/'; CTRL:'/') ); SDLDosColor : Array[0..15] of TSDL_Color = ( (R:000; G:000; B:000; Unused: 0), //00 (R:000; G:000; B:128; Unused: 0), //01 (R:000; G:128; B:000; Unused: 0), //02 (R:000; G:128; B:128; Unused: 0), //03 (R:170; G:000; B:000; Unused: 0), //04 (R:128; G:000; B:128; Unused: 0), //05 (R:128; G:128; B:000; Unused: 0), //06 (R:192; G:192; B:192; Unused: 0), //07 (R:128; G:128; B:128; Unused: 0), //08 (R:000; G:000; B:255; Unused: 0), //09 (R:000; G:255; B:000; Unused: 0), //10 (R:000; G:255; B:255; Unused: 0), //11 (R:255; G:000; B:000; Unused: 0), //12 (R:255; G:000; B:255; Unused: 0), //13 (R:255; G:255; B:000; Unused: 0), //14 (R:255; G:255; B:255; Unused: 0) //15 ); Constructor TSDLConsole.Create (InitMode: TSDLScreenMode); Begin Inherited Create; SDL_INIT(SDL_INIT_VIDEO); // Screen := SDL_SetVideoMode(SDLAppWindowX, SDLAppWindowY, 32, SDL_HWSURFACE or SDL_FULLSCREEN); Screen := SDL_SetVideoMode(SDLAppWindowX, SDLAppWindowY, 32, SDL_HWSURFACE); If Screen = NIL Then Halt; If TTF_Init = -1 Then Halt; // Font := TTF_OpenFont('ASCII.ttf', SDLFontSize); Font := TTF_OpenFont('\dev\sdl\Perfect DOS VGA 437.ttf', SDLFontSize); If Font = NIL Then Halt; New (InputEvent); InputSize := 0; InputPos := 0; CursorX := 1; CursorY := 1; TextAttr := 7; End; Destructor TSDLConsole.Destroy; Begin Dispose (InputEvent); TTF_CloseFont(Font); TTF_Quit; SDL_QUIT; Inherited Destroy; End; Procedure TSDLConsole.PushInput (Ch: Char); Begin Inc (InputSize); If InputSize > AppInputSize Then Begin InputSize := 1; InputPos := 0; End; InputBuffer[InputSize] := Ch; End; Procedure TSDLConsole.PushExt (Ch: Char); Begin PushInput(#0); PushInput(Ch); End; Procedure TSDLConsole.PushStr (Str: String); Begin PushInput (Str[1]); If Length(Str) > 1 Then PushInput (Str[2]); End; Procedure TSDLConsole.ProcessEvent; Var IsShift : Boolean = False; IsCaps : Boolean = False; IsAlt : Boolean = False; IsCtrl : Boolean = False; Found : Boolean; Count : Integer; Begin IsShift := (InputEvent^.Key.KeySym.Modifier AND KMOD_SHIFT <> 0); IsCaps := (InputEvent^.Key.KeySym.Modifier AND KMOD_CAPS <> 0); IsAlt := (InputEvent^.Key.KeySym.Modifier AND KMOD_ALT <> 0); IsCtrl := (InputEvent^.Key.KeySym.Modifier AND KMOD_CTRL <> 0); Case InputEvent^.Type_ of SDL_KEYDOWN : Begin Case InputEvent^.Key.KeySym.Sym of SDLK_A.. SDLK_Z : Begin If IsShift or IsCaps Then Dec (InputEvent^.Key.KeySym.Sym, 32); PushInput (Chr(InputEvent^.Key.KeySym.Sym)); End; SDLK_DELETE : PushExt(keyDELETE); SDLK_UP : PushExt(keyUP); SDLK_DOWN : PushExt(keyDOWN); SDLK_RIGHT : PushExt(keyRIGHT); SDLK_LEFT : PushExt(keyLEFT); SDLK_INSERT : PushExt(keyINSERT); SDLK_HOME : PushExt(keyHome); SDLK_END : PushExt(keyEnd); SDLK_PAGEUP : PushExt(keyPGUP); SDLK_PAGEDOWN : PushExt(keyPGDN); SDLK_NUMLOCK.. SDLK_COMPOSE : //ignore mod keys; Else Found := False; For Count := 1 to SDLKeyMapSize Do If InputEvent^.Key.KeySym.Sym = SDLKeyMapUS[Count].SDL Then Begin If IsShift Then PushStr(SDLKeyMapUS[Count].Shift) Else If IsAlt Then PushStr(SDLKeyMapUS[Count].Alt) Else If IsCTRL Then PushStr(SDLKeyMapUS[Count].CTRL) Else PushStr(SDLKeyMapUS[Count].Key); Found := True; Break; End; If Not Found Then PushInput(Chr(InputEvent^.Key.KeySym.Sym)); End; End; SDL_QUITEV : Halt; End; End; Function TSDLConsole.KeyPressed : Boolean; Begin If SDL_PollEvent(InputEvent) > 0 Then ProcessEvent; Result := InputPos <> InputSize; End; Function TSDLConsole.ReadKey : Char; Begin If InputPos = InputSize Then Repeat SDL_WaitEvent(InputEvent); ProcessEvent; Until (InputSize <> InputPos); Inc (InputPos); Result := InputBuffer[InputPos]; If InputPos = InputSize Then Begin InputPos := 0; InputSize := 0; End; End; Procedure TSDLConsole.Delay (MS: LongInt); Begin SDL_DELAY(MS); End; Procedure TSDLConsole.SetTitle (Title: String); Begin Title := Title + #0; SDL_WM_SetCaption(PChar(@Title[1]), PChar(@Title[1])); End; Procedure TSDLConsole.TestStuff; Var Rect : TSDL_Rect = (X:0; Y:0; W:0; H:0); Surface : PSDL_Surface; Text : String; Count : Byte; Begin Text := #176 + 'SDL Demo! Press Escape to quit!' + #0; Surface := TTF_RenderText_Shaded (Font, PChar(@Text[1]), SDLDosColor[7], SDLDosColor[0]); SDL_BlitSurface (Surface, NIL, Screen, @Rect); SDL_FreeSurface (Surface); For Count := 3 to 17 Do Begin Text := #219 + '2345678901234567890123456789012345678901234567890123456789012345678901234567890'; Rect.Y := (Count - 1) * SDLFontSize + SDLFontSpace; Surface := TTF_RenderText_Shaded (Font, PChar(@Text[1]), SDLDosColor[Count-2], SDLDosColor[0]); SDL_BlitSurface (Surface, NIL, Screen, @Rect); SDL_FreeSurface (Surface); End; For Count := 18 to 25 Do Begin Text := #219 + '2345678901234567890123456789012345678901234567890123456789012345678901234567890'; Rect.Y := (Count - 1) * SDLFontSize + SDLFontSpace; Surface := TTF_RenderText_Shaded (Font, PChar(@Text[1]), SDLDosColor[7], SDLDosColor[0]); SDL_BlitSurface (Surface, NIL, Screen, @Rect); SDL_FreeSurface (Surface); End; SDL_Flip(Screen); End; Procedure TSDLConsole.ShowBuffer; Begin SDL_Flip(Screen); End; End.