Inspired by OpenConsole

BlitzMax Forums/BlitzMax Programming/Inspired by OpenConsole

Not a total ripoff though. Mostly rewritten with more features/simpler interface.

Just call Init, then Update in your main loop. Update will check the TAB key.

New features are: Two consoles, an active and a passive, execute console script files, dump console to file, easier command add/execute, default commands: clear, echo, exec, dumpconsole, cmdlist.

SuperStrict

' Console by Odin Jensen
' Use as you please :)
' Thanks to Perturbatio for the OpenConsole source.

Type TConsole

	' Console active flag
	Field isActive% = False
	
	' Command map
	Field commands:TMap = New TMap
	
	' Passive console text
	Field passiveText:TList = New TList
	
	' Active console text
	Field conText:TList = New TList
	
	' Command history
	Field commandHistory:TList = New TList
	
	' Size
	Field conWidth%, conLines%
	
	' Console list position
	Field conPos% = 0
	
	' Passive console list position
	Field conPassivePos% = 0
	
	' Input field
	Field inputField$ = ""
	
	' Cursorpos
	Field cursorPos% = 0
	
	' History pos
	Field historyPos% = -1

	' Init console
	Method Init(width%, lines%)
	
		' Save size
		conWidth = width
		conLines = lines
			
		?debug
		
			AddPassiveConsoleText("*** Debug build ***")
			AddActiveConsoleText("*** Debug build ***")					
		?
		
		
		AddPassiveConsoleText("Press <TAB> to activate console")
		AddActiveConsoleText("Press <TAB> to hide console")

		AddActiveConsoleText("Type 'cmdlist' for list of commands.")
		
	
	End Method
	
	' Is active?
	Method Active%()
	
		Return isActive
	
	End Method
	
	' Update console
	Method Update()			
	
		' Set text color
		SetColor(255,255,255)	
	
		' Check for console activation key
		If (KeyHit(KEY_TAB))
		
			isActive = Not isActive
			FlushKeys()
		
		End If
		
		' Active?
		If (isActive)
				
			' Draw console
			SetColor(0,0,255)
			DrawRect(0, 0, conWidth, conLines*12)
			
			SetColor(255,255,255)	
			
			' Draw input string
			DrawText(inputField, 5, 5)

			' Draw cursor			
			DrawText("_", TextWidth(inputField[..CursorPos+1])-2, 5)

			
			' Draw active console text
			Local curLine%=0
			For Local i% = conPos To conText.Count()-1
			
				DrawText(String(conText.ValueAtIndex(i)), 5, 5+12+(curLine*12))
				curLine:+1
				If (curLine > conLines-3)
				
					Exit
				
				End If
			
			Next		
			
			' Draw scroll markers?
			If (conPos > 0)
			
				DrawText("<<", conWidth-(12*2), 0)
			
			End If
			If (conPos +22<=conText.Count())
			
				DrawText(">>", conWidth-(12*2), (conLines-1)*12)

			End If
		
		
		Else
		
			' Draw passive console text
			Local curLine%=0
			For Local i% = conPassivePos To passiveText.Count()-1
			
				DrawText(String(passiveText.ValueAtIndex(i)), 5, 5+(curLine*12))
				curLine:+1
				If (curLine > conLines)
				
					Exit
				
				End If
			
			Next
			
			' Draw scroll markers?
			If (conPassivePos > 0)
			
				DrawText("<<", conWidth-(12*2), 0)
			
			End If
			If (conPassivePos+22<=passiveText.Count())
			
				DrawText(">>", conWidth-(12*2), conLines*12)

			End If
		
		End If
		
		' Do an input check
		CheckInput()
	
	End Method
	
	' Check input
	Method CheckInput()
	
		' Active?
		If (isActive)
		
			' Do passive scroll?
			If (KeyHit(KEY_PAGEUP))
				
				If (conPos > 0)
					conPos :-1
				End If			
			
			ElseIf (KeyHit(KEY_PAGEDOWN))
			
				If (conPos <= (conText.Count()-2-conLines))
					conPos :+1
				End If	
			
			End If
			
			Local ch:Int = GetChar()
		
			If ((ch > 31) And (ch < 126))
				inputField=inputField[..CursorPos]+Chr(ch)+inputField[CursorPos..]':+Chr(ch)
				CursorPos:+1
			EndIf
			
			Select (ch)
				Case 8 'Backspace
					If (CursorPos > 0)
					
						inputField=inputField[..CursorPos-1]+inputField[CursorPos..] 'backspace
						CursorPos:-1
						
					EndIf
				
				Case 13 'Return
					If (Len(inputField)>0)
					
						Local cmdList:TList = SplitString(inputField, " ")
						Local args:String[] = New String[cmdList.Count()-1]
						Local cmd:String = String(cmdList.ValueAtIndex(0))
						
						If (cmdList.Count() > 1)
						
							For Local i%=1 To cmdList.Count()-1
							
								args[i-1] = String(cmdList.ValueAtIndex(i)).toLower()
							
							Next
							
						End If
						ExecuteCommand(cmd, args)
						commandHistory.AddFirst(inputField)						
						
					EndIf
					
					inputField = "" 'return
					CursorPos = 0
					historyPos = -1
								
				Case 27 'Escape
				
					inputField = "" 'escape
					CursorPos = 0	
					historyPos = -1				
					
			End Select
			
			' Check special keys
			Local SpecialKey% = GetKey()
			
			Select (SpecialKey)
			
				Case KEY_LEFT 
				
					If (CursorPos>0) CursorPos:- 1
					
				Case KEY_RIGHT
				
					If (CursorPos < Len(inputField)) CursorPos:+ 1
					
				Case KEY_DELETE
				
					If (CursorPos < Len(inputField))
						inputField= inputField[..CursorPos]+inputField[CursorPos+1..]
					EndIf
					
				Case KEY_END
				
					CursorPos = Len(inputField)
					
				Case KEY_HOME
				
					CursorPos = 0		
					
				Case KEY_UP
				
					If (commandHistory.Count() > 0)
						If (historyPos < commandHistory.Count()-1) historyPos:+ 1
						inputField= String(commandHistory.ValueAtIndex(historyPos))
						CursorPos = Len(inputField)
					EndIf
				
				Case KEY_DOWN
				
					If (commandHistory.Count() > 0)
					If (historyPos > -1) HistoryPos:- 1
					If (historyPos < 0)
						inputField= ""
					Else
						inputField= String(commandHistory.ValueAtIndex(historyPos ))
					EndIf
					CursorPos = Len(inputField)
					
				EndIf

					
			End Select						
		
		Else
		
			' Do passive scroll?
			If (KeyHit(KEY_PAGEUP))
				
				If (conPassivePos > 0)
					conPassivePos:-1
				End If			
			
			ElseIf (KeyHit(KEY_PAGEDOWN))
			
				If (conPassivePos <= (passiveText.count()-2-conLines))
					conPassivePos:+1
				End If	
			
			End If
			
		
		End If
			
	End Method
	
	' Add command
	Method AddCommand(command$, help$, Action(parms$[]))
	
		Local newCommand:TConsoleCommand = New TConsoleCommand
		newCommand.desc = help
		newCommand.Action = Action
		MapInsert(commands, command.toLower(), newCommand)
	
	End Method
	
	' Execute command
	Method ExecuteCommand(command$, parms$[])
		
		Local cmd:TConsoleCommand = TConsoleCommand(MapValueForKey(commands, command.toLower()))
		If (cmd = Null)
		
			AddActiveConsoleText("Unknown command: " + command + "!")
			Return
		
		End If
		
		cmd.Action(parms)
		
	
	End Method
	
	' Add text to active console
	Method AddActiveConsoleText(text$)
	
		conText.addLast(text)
		If (conText.Count() > conLines-1)
		
			conPos:+1
		
		End If

	End Method
	
	' Add text to passive console
	Method AddPassiveConsoleText(text$)
	
		passiveText.addLast(text)
		If (passiveText.Count() > conLines)
		
			conPassivePos:+1
		
		End If
	
	End Method
	
	' Add to visible console
	Method AddVisibleConsoleText(text$)
	
		If (isActive)
		
			AddActiveConsoleText(text)
		
		Else
		
			AddPassiveConsoleText(text)
		
		End If
	
	End Method
	
	' *** Commands ***
	
	' List commands
	Method cmdListCommands()
	
		Local i:TMapEnumerator = commands.Values()
		For Local curCmd:TConsoleCommand=EachIn i
		
			AddActiveConsoleText(curCmd.desc)
		
		Next
	
	End Method
	
	' Clear console
	Method cmdClearConsole(parms$[])
	
		If (parms.length > 0)
		
			Select (parms[0])
			
				Case "active"
				
					conPos = 0
					conText.clear()
					commandHistory.clear()
					Return
				
				Case "passive"
				
					conPassivePos = 0
					passiveText.clear()
					Return								
			
			End Select
		
		End If
		
		conPassivePos = 0
		conPos = 0
		passiveText.clear()
		conText.clear()
	
	End Method
	
	' Echo text
	Method cmdEcho(parms$[])
	
		If (parms.length = 0)
		
			AddActiveConsoleText("You need to specify some text to echo.")
			Return
						
		End If
		
		Local out$ = ""
		For Local i%=0 To parms.length-1
		
			out:+" "+parms[i]
		
		Next
		
		AddActiveConsoleText(out[1..out.length])
	
	End Method
	
	' Dump console
	Method cmdDumpConsole(parms$[])
	
		Local fh:TStream = WriteFile("ConsoleDump.txt")
		If (fh)
		
			WriteLine(fh, "[Passive Console]")
			For Local pct:String=EachIn passiveText
			
				WriteLine(fh, pct)
			
			Next
			
			WriteLine(fh, "~n[Active Console]")
			For Local pct:String=EachIn conText
			
				WriteLine(fh, pct)
			
			Next

		
			CloseStream(fh)
			
			AddActiveConsoleText("Dumped consoles to 'ConsoleDump.txt'.")
		
		Else
		
			AddActiveConsoleText("Could not write 'ConsoleDump.txt'. Write protected media?")
		
		End If
	
	End Method
	
	' Execute script command
	Method cmdExecFile(args$[])
	
		If (args.length = 0)
		
			AddActiveConsoleText("You need to specify a file to execute.")
			Return
						
		End If
		
		Local fh:TStream = OpenFile(args[0])
		If (fh = Null)
		
			AddActiveConsoleText("File '" + args[0] + "' not found!")
			Return
		
		End If
		
		While (Not Eof(fh))
		
			Local cmdList:TList = SplitString(ReadLine(fh), " ")
			Local fargs:String[] = New String[cmdList.Count()-1]
			Local cmd:String = String(cmdList.ValueAtIndex(0))
			
			If (cmdList.Count() > 1)
			
				For Local i%=1 To cmdList.Count()-1
				
					fargs[i-1] = String(cmdList.ValueAtIndex(i)).toLower()
				
				Next
				
			End If

			ExecuteCommand(cmd, fargs)
		
		Wend

		CloseStream(fh)
	
	End Method
	
	
End Type

' Console command type
Type TConsoleCommand

	Field desc:String
	Field Action(parms$[])

End Type

' Split string used for splitting console commands and args
Function SplitString:TList(inString:String, Delim:String)
	Local tempList : TList = New TList
	Local currentChar : String = ""
	Local count : Int = 0
	Local TokenStart : Int = 0
	
	If Len(Delim)<>1 Then Return Null
	
	inString = Trim(inString)
	
	For count = 0 Until Len(inString)
		If inString[count..count+1] = delim Then
			tempList.AddLast(inString[TokenStart..Count])
			TokenStart = count + 1
		End If
	Next
	tempList.AddLast(inString[TokenStart..Count])	
	Return tempList
End Function


' Get key utility function
Function GetKey:Int()

	Const RepeatRate : Int = 100 'ms
	Global LastTime:Long = MilliSecs()
	Global LastKey:Int = 0
	Local key:Int = 0
		
	While  Key <= 226
		If KeyDown(key) Then Exit
		Key:+ 1
	Wend
	
	
	If Key = 227 Then Return 0
	
	If Key = LastKey Then
		If MilliSecs()-LastTime < RepeatRate Then Return 0
	EndIf
	
	LastTime = MilliSecs()
	LastKey = Key
	If Key < 227 Then Return Key Else Return 0
	
End Function

' Create console
Global con:TConsole = New TConsole

' Commands
Function cmdCmdList(parms$[])
	con.cmdListCommands()
End Function

Function cmdClearConsole(parms$[])
	con.cmdClearConsole(parms)
End Function

Function cmdEcho(parms$[])
	con.cmdEcho(parms)
End Function

Function cmdDumpConsole(parms$[])
	con.cmdDumpConsole(parms)
End Function

Function cmdExecFile(parms$[])
	con.cmdExecFile(parms)
End Function


' Add commands
con.AddCommand("cmdlist", "cmdlist - List all available commands.", cmdCmdList)
con.AddCommand("clear", "clear [active|passive] - Clears both consoles, unless one is specified.", cmdClearConsole)
con.AddCommand("echo", "echo text - Echo text to console.", cmdEcho)
con.AddCommand("dumpconsole", "dumpconsole - Dumps console text to 'ConsoleDump.txt'.", cmdDumpConsole)
con.AddCommand("exec", "exec <filename> - Executes a console script file.", cmdExecFile)



Small test script you can 'exec'
echo Hello!
echo This is an executed test script!
clear passive
echo Cleaned passive console yeah!


' Thanks to OpenConsole guy (forgot name)


That would be me :)

Ahh right. Thanks to Perturbatio for the OpenConsole source :)

Can I get an example of usage of this? I know I know, I'm failing the IQ test here. Thaaaaaanks!

Yep. In your max2D based application, create an instance of TConsole.
Then call Init(width, height)

Then just call Update in your main loop. Press TAB to enter the console and type cmdlist.

See the bottom of the source file for examples on how to add your own commands.