Non-Blocking Console Input

Non-blocking console input C++

I would do this by creating separate a thread which calls normal blocking IO functions and pass it a callback function which it would call when it got input. Are you sure you need to do what you said you want to do?

As for outputting information at the same time, what would happen if the user was in the middle of typing some input and you printed something?

Non-blocking console input?

For Windows, console only, use the msvcrt module:

import msvcrt

num = 0
done = False
while not done:
print(num)
num += 1

if msvcrt.kbhit():
print "you pressed",msvcrt.getch(),"so now i will quit"
done = True

For Linux, this article describes the following solution, it requires the termios module:

import sys
import select
import tty
import termios

def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])

old_settings = termios.tcgetattr(sys.stdin)
try:
tty.setcbreak(sys.stdin.fileno())

i = 0
while 1:
print(i)
i += 1

if isData():
c = sys.stdin.read(1)
if c == '\x1b': # x1b is ESC
break

finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)

For cross platform, or in case you want a GUI as well, you can use Pygame:

import pygame
from pygame.locals import *

def display(str):
text = font.render(str, True, (255, 255, 255), (159, 182, 205))
textRect = text.get_rect()
textRect.centerx = screen.get_rect().centerx
textRect.centery = screen.get_rect().centery

screen.blit(text, textRect)
pygame.display.update()

pygame.init()
screen = pygame.display.set_mode( (640,480) )
pygame.display.set_caption('Python numbers')
screen.fill((159, 182, 205))

font = pygame.font.Font(None, 17)

num = 0
done = False
while not done:
display( str(num) )
num += 1

pygame.event.pump()
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
done = True

How to do non-blocking keyboard input on console app using Swift?

The termios functions translate almost one-to-one to Swift:

#if os(Linux)
import Glibc
#else
import Darwin
#endif

var orig_termios = termios()

func reset_terminal_mode() {
tcsetattr(0, TCSANOW, &orig_termios)
}

func set_conio_terminal_mode() {
tcgetattr(0, &orig_termios)
var new_termios = orig_termios
atexit(reset_terminal_mode)
cfmakeraw(&new_termios)
tcsetattr(0, TCSANOW, &new_termios)
}

set_conio_terminal_mode()

The problem with select() is that FD_ZERO etc are “non-trivial” macros and not imported into Swift. But you can use poll() instead:

func kbhit() -> Bool {
var fds = [ pollfd(fd: STDIN_FILENO, events: Int16(POLLIN), revents: 0) ]
let res = poll(&fds, 1, 0)
return res > 0
}

An alternative is to use the Dispatch framework. Here is a simple example which might help you get started. A dispatch source is used to wait asynchronously for available input, which is then appended to an array, from where it is retrieved in the getch() function. A serial queue is used to synchronize access to the array.

import Dispatch

let stdinQueue = DispatchQueue(label: "my.serial.queue")
var inputCharacters: [CChar] = []

let stdinSource = DispatchSource.makeReadSource(fileDescriptor: STDIN_FILENO, queue: stdinQueue)
stdinSource.setEventHandler(handler: {
var c = CChar()
if read(STDIN_FILENO, &c, 1) == 1 {
inputCharacters.append(c)
}
})
stdinSource.resume()

// Return next input character, or `nil` if there is none.
func getch() -> CChar? {
return stdinQueue.sync {
inputCharacters.isEmpty ? nil : inputCharacters.remove(at: 0)
}
}

Cin without waiting for input?

It's sad that there is no simple portable way to checking asynchronously if a key was hit. But I guess that the standard committee has carefully evaluated the pros and cons.

If you don't want to rely on third party event management libraries, and if multithreading would be overkill, one alternative could be to have your own version of kbhit(), with conditional compiling for the environments you want to support:

  • if your conio.h supports kbhit() just use it.
  • for windows, you can refer to _kbhit()
  • for linux and posix, you can use Matthieu's answer, or look here for Morgan Mattews's code

It's not the most academic answer, but it's pragmatic.

Getting input from console in Java: Is it blocking or non-blocking?

Just compare these two samples:

Frist, linear without multiple threads:

public class SampleClass {

public static void main(String[] args) {
SampleClass instance = new SampleClass();
instance.start();
}

private void start() {
Scanner sc = new Scanner(System.in);
String input;

while (!(input = sc.nextLine()).equals("exit")) {
processInput(input);
}

sc.close();
}

private void processInput(String input) {
try {
Thread.sleep(2000);
System.out.println("input: " + input);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

Then, using a new thread for every input processing:

public class SampleClass {

public static void main(String[] args) {
SampleClass instance = new SampleClass();
instance.start();
}

private void start() {
Scanner sc = new Scanner(System.in);
String input;

while (!(input = sc.nextLine()).equals("exit")) {
processInput(input);
}

sc.close();
}

private void processInput(String input) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("input: " + input);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}

}

Just try it. In both cases you don't miss input, but the difference is that without new threads the processing time adds up (of course) - it doesn't run parallel.

Non-blocking ReadConsoleInput

This is all documented in ReadConsoleInput. You can determine if there is a console input with GetNumberOfConsoleInputEvents. And you are able to to determine the type of console input events with PeekConsoleInput.

So GetNumberOfConsoleInputEvents is all you need.

You can also use WaitForSingleObject with the console handle to wait for a next available input. This is also documented in ReadConsoleInput

Non-Blocking read from standard I/O in C#

var buf=new byte[2048];
var inputStream=Console.OpenStandardInput(); //dispose me when you're done
inputStream.BeginRead(buf,0,buf.Length,ar=>{
int amtRead=inputStream.EndRead(ar);
//buf has what you need. You'll need to decode it though
},null);


Related Topics



Leave a reply



Submit