Asynchronous console input

Share your advanced PureBasic knowledge/code with the community.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Asynchronous console input

Post by Lunasole »

The Input() locks program, but seems that without it you can only use threads to make program async, or do all the input handling and displaying by yourself.

I don't know is it right to do things like following (implementing own base editing functions for every key), but anyway I did it for fun while playing with console a bit ^^

Code: Select all

EnableExplicit

If OpenConsole()
	PrintN("Type something and try to delete it using backspace & moving arrow keys")
	
	Define Buffer$, BufferSize, BufferPos
	Define VK, Key$
	Define Back
	
	Repeat
		Key$ = Inkey()
		VK = RawKey()


		Select VK
			Case #VK_RETURN:
				; display buffer content (receive collected input with enter)
				ConsoleTitle(Buffer$)
				
				; clear current line
				Print(Chr(#VK_RETURN))
				Print(Space(BufferSize))
				Print(Chr(#VK_RETURN))
				; or can simply jumt to next line
; 				PrintN("")
				
				; reset buffer
				Buffer$ = ""
				BufferSize = 0
				BufferPos = 0
		
			Case #VK_BACK:
				; display: move by 1 char left and erase it (replace by space)
; 				Print(Chr(#VK_BACK))
; 				Print(" " + Chr(#VK_BACK))
				
				; edit buffer
				If BufferSize
					Print(Chr(#VK_RETURN))
					Print(Space(BufferSize))
					Print(Chr(#VK_RETURN))	         	
					
					
					Buffer$ = Left(Buffer$, BufferPos - 1) + Right(Buffer$, BufferSize - BufferPos)
					BufferSize - 1
					BufferPos - 1
					Print(Buffer$)
					
					Back = BufferSize - BufferPos
					While Back
						Print(Chr(#VK_BACK))
						Back - 1
					Wend
				EndIf
			Case #VK_DELETE:
				; TODO
				
			Case #VK_LEFT:
				; move cursor left
				If BufferPos
					Print(Chr(#VK_BACK))
					BufferPos - 1
				EndIf
				
			Case #VK_RIGHT:
				; move cursor right
				If BufferPos <= BufferSize - 1
					BufferPos + 1
					Print(Mid(Buffer$, BufferPos, 1))
				EndIf

			Default: 
				; add regular key to a buffer
				If Key$
					If Not BufferSize > 64
						Buffer$ = Left(Buffer$, BufferPos) + Key$ + Right(Buffer$, BufferSize - BufferPos)
						BufferSize + 1
						Print(Right(Buffer$, BufferSize - BufferPos))
						BufferPos + 1
						
						Back = BufferSize - BufferPos
						While Back
							Print(Chr(#VK_BACK))
							Back - 1
						Wend
					EndIf
				Else
					Delay(32)
				EndIf
		EndSelect

	ForEver
EndIf
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Asynchronous console input

Post by es_91 »

PB console function input () is very static. It locks the "pipe". I do not think a thread could change the console while inputing ()... should crash probably.
do all the input handling and displaying by yourself
That is what you should do. I once wrote a CLI GUI handling include. Sorry, i do not know where it is. If i find soon, i'll post, if you could use with.

By-the-way, two or three hours ago you posted a similar-topic question and I really had a helpful answer for you. I know, you got it by yourself, but erasing threads is kind of a sad thing to do if you can re-use them. :wink:
:mrgreen:
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Asynchronous console input

Post by Lunasole »

es_91 wrote: By-the-way, two or three hours ago you posted a similar-topic question and I really had a helpful answer for you. I know, you got it by yourself, but erasing threads is kind of a sad thing to do if you can re-use them. :wink:
That was the same topic but in question form ^^ Sorry if you going to post to it, I've removed it in few minutes after creation as I thought that question was too obvious and coded what posted here.

Btw that example has problem when buffer size becomes larger > console line width.
If you remove following limitation, the things going bad:

Code: Select all

If Not BufferSize > 64
Maybe you remember how you solved this in your code?
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Asynchronous console input

Post by es_91 »

Please describe that more closely. I notice so far no errors lifting up the buffer size.
:mrgreen:
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Asynchronous console input

Post by Lunasole »

es_91 wrote:Please describe that more closely. I notice so far no errors lifting up the buffer size.
If only typing something, all is OK. The problem for example if using Backspace key with buffer > line width.
So generally following questions:
- How to return cursor to a previous line?
- How to know console line width?
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Asynchronous console input

Post by es_91 »

I think i saw what you mean: the display doubles with all content into a new line when pushing backspace in a following line to the first.
Lunasole wrote:- How to return cursor to a previous line?
- How to know console line width?
Look:

ConsoleLocate () ... is what you need !
ClearConsole () ... might come in handy, as well.

NewList and AddElement () should do your buffering job, if you want to learn it the "proper" and easy way. See "List" library.

And finally, if you jump inside your console, use EnableGraphicalConsole ().
:mrgreen:
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Asynchronous console input

Post by es_91 »

Wait 20 minutes, please. I'll code you an example using lists and graphical mode.
:mrgreen:
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Asynchronous console input

Post by Lunasole »

es_91 wrote:Wait 20 minutes, please. I'll code you an example using lists and graphical mode.
That should take much more than 20m :)
I was thinking about graphical mode and it is clear with it if that's only solution. I just though it is possible to perform multi-line edit without using it (some control code to jump line back?).
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Asynchronous console input

Post by es_91 »

Not to my knowledge, sorry.

Here's my prospect given:

Code: Select all

enableExplicit


#CWIDTH  =  80
#CHEIGHT  =  25

#KEY_SPACE = 32


define key$ { 1 } 				; primitive string for educational purpose (static)
define key. w 					; primitive word for educational purpose
define quitSwitch. b 			; primitive byte for educational purpose (boolean use)
define cursorPosition. w		; primitive word for educational purpose
define temporaryPosition. w 	; primitive word for educational purpose

dim characterBuffer. c ( #CWIDTH  *  #CHEIGHT ) 	; primitive character for educational purpose

newList characterHistory. c (  ) 	; primitive character for educational purpose


for temporaryPosition = 1 to 80 * 25
	
	characterBuffer ( temporaryPosition ) = #KEY_SPACE
	
next


procedure updateCursor ()
	
	shared cursorPosition
	
	
	consoleLocate ( cursorPosition  -  int ( 1.0  *  cursorPosition  /  #CWIDTH )  *  #CWIDTH, int ( 1.0  *  cursorPosition  /  #CWIDTH ) )
	
endProcedure

procedure redrawConsole ()
	
	shared characterBuffer (  )
	
	protected line. b; 		primitive byte for educational purpose
	
	protected column. b; 	primitive byte for educational purpose
	
	protected temporaryCharacterChain$ { #CWIDTH }; 	primitive string for educational purpose (static)
	
	
	for line  =  1 to #CHEIGHT
		
		temporaryCharacterChain$  =  #empty$
		
		for column  =  1 to #CWIDTH
			
			temporaryCharacterChain$  =  temporaryCharacterChain$  +  chr ( characterBuffer ( #CWIDTH * ( line  -  1 ) + column  -  1 ) )
			
		next
		
		consoleLocate ( 0, line  -  1 )
		print ( temporaryCharacterChain$ )
		
	next
	
	
	updateCursor (  )
	
endProcedure

procedure addNewSign ()
	
	shared characterBuffer (  )
	
	shared cursorPosition
	
	shared key$
	
	
	if cursorPosition < ( #CWIDTH  *  #CHEIGHT )
		
		characterBuffer ( cursorPosition )  =  asc ( key$ )
		
		cursorPosition  =  cursorPosition  +  1
		
		redrawConsole (  )
		
	endIf
	
endProcedure


openConsole (  )


enableGraphicalConsole ( #true )

consoleColor ( 0, 7 )
printn ( "The whole console will be your input area.")
printn ( "Only letters and numbers are allowed! Type 'close' to exit." )
consoleLocate ( 0, ( #CHEIGHT  /  2 ) )
printn ( "1        1x        2x        3x        4x        5x        6x 3 5 7  7x 3 5 7 9 " )
consoleLocate ( 0, ( #CHEIGHT  -  1 ) )
print ( "Just press <Enter> or <Return> to start." )

consoleColor ( 7, 7 )

if ( lCase ( input (  ) ) = "close" )
	
	quitSwitch  =  #true
	
endIf

consoleColor ( 7, 0 )

clearConsole (  )


while ( not quitSwitch )
	
	key$ = inkey (  )
	key = rawKey (  )
	
	if ( ( not asc ( key$ ) ) and ( not key ) ); 	[AUTHOR0] both zero-set means no input/inkey
		
		delay ( 40 )
		
	else
		
		if ( ( key$ = "" ) or ( ( uCase ( key$ ) = key$ ) and ( not ( str ( val ( key$ ) ) = key$ ) ) ) )
			
			select key
					
				case 8
					
					if cursorPosition > 0
						
						characterBuffer ( cursorPosition - 1 )  =  #KEY_SPACE
						
						cursorPosition  =  cursorPosition  -  1
						
						redrawConsole (  )
						
					endIf
					
				case 27
					
					quitSwitch  =  #true
					
				case 32
					
					addNewSign (  )
					
				case 37
					
					if cursorPosition > 1
						
						cursorPosition  =  cursorPosition  -  1
						
						redrawConsole (  )
						
					endIf
					
				case 38
					
					if cursorPosition > #CWIDTH
						
						cursorPosition  =  cursorPosition  -  #CWIDTH
						
						redrawConsole (  )
						
					endIf
					
				case 39
					
					if cursorPosition < ( #CWIDTH  *  #CHEIGHT )
						
						cursorPosition  =  cursorPosition  +  1
						
						redrawConsole (  )
						
					endIf
					
				case 40
					
					if cursorPosition < ( #CWIDTH  *  ( #CHEIGHT  -  1 ) )
						
						cursorPosition  =  cursorPosition  +  #CWIDTH
						
						redrawConsole (  )
						
					endIf
					
				default
					
					debug key
					
			endSelect
			
		else
			
			addNewSign (  )
			
		endIf
		
	endIf
	
wEnd


enableGraphicalConsole ( #false )


end
Okay... unfinished, imperfect and took 2 hours. Phew ! :!:

BTW: How do these guys come up with some 10'000 lines include codes just around the corner saying "here's my implementation" ... you guys work over-night ?? :o :| :wink:

/ed: it's actually noth'n but a rough toy, but hey, i made a rough toy ! :mrgreen: - i MADE This ... many like 'em, but this one... 's mine. :) ^^
:mrgreen:
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Asynchronous console input

Post by Lunasole »

es_91 wrote: /ed: it's actually noth'n but a rough toy, but hey, i made a rough toy ! :mrgreen: - i MADE This ... many like 'em, but this one... 's mine. :) ^^
Kind of familiar feeling, I have whole separated folder full of "rough toys" ^^ That's nice generally, sometimes they are very useful as starting point if going to do something real.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Post Reply