Console input is interupted by output - kotlin

I have a Kotlin console application with a lot of log messages.
For testing, I want to enter certain admin commands in the console. However, each log message apparently erases my console input buffer.
How can I reliably read a line or even just individual chars from the console, while other threads are printing log messages to that console?
fun main() {
thread(isDaemon=false, name = "ConsoleThread") {
while(true) {
println("(doit) do something")
println("(exit) shutdown")
val line = readLine()?:break
when (line) {
"doit" -> println("doing something...")
"exit" -> System.exit(0)
else -> println("command not recognized: '$line'")
}
}
}
thread(isDaemon=true, name = "WorkerThread") {
while (true){
println("another log message...")
Thread.sleep(500)
}
}
}
When I execute this test program, if typing a word takes more than 500ms, the beginning of the word is erased.
I tried readline, Scanner, InputStreamReader, System.in.read(), nothing seems to work.
Tested in IntelliJ Idea, Windows 10

Related

Is there a way to update the text of the previous line in a console application running in Kotlin?

#Test
fun updateLastLine() {
System.out.print("First Text")
System.out.print("\rSecond Text")
System.out.print("\n")
// Console Output (already working as intended)
// Second Text
}
#Test
fun updatePreviousLineWithCarriageReturn() {
System.out.print("Actual Update\n")
System.out.print("Last Line\n")
System.out.print("\rExpected Update")
System.out.print("\n")
// Console Output (Expected)
// Expected Update
// Last Line
// Console Output (Actual)
// Actual Update
// Last Line
// Expected Update
}
#Test
fun updatePreviousLineWithBackSpace() {
System.out.print("Actual Update\n")
System.out.print("Last Line\n")
System.out.print("\b\b\b\b\b\b\b\b\b\bExpected Update")
System.out.print("\n")
// equal updatePreviousLineWithCarriageReturn
}
I am using the terminal within Intelllj Community,
I could update the last line of text printed to the console using a carriage return.
However, the previous text that was moved to the next line using \n could not be modified in any way.
I couldn't find a reference that explained how to fix the old text after jumping to a new line.
Is there any way to change the text of the previous line?

Double subscriptions on inner flow with kotlin-flow

I have the following problem with my code snippet below;
When the set of currently connected barcode-scanners changes, a set with the currently connected scanners will be emitted by the scanners: Flow<Set<BarcodeScanner>> flow.
If I have connected a single scanner, called A, and I scan a barcode this works fine and on the result will be shown on the text-view.
If I connect an additional device, and then scan with device A again, I will see the scan result 2 times.
It looks like that on every change of the Set<> an additional flow subscription is created in the collect { scanner.scan() } and the previous flow is still in place.
How can I solve this? I already played the whole day with different operators etc...
I'm using Flow 1.2.2
uiScope.launch { //CoroutineScope(Job() + Dispatchers.Main)
scanners //Flow<Set<BarcodeScanner>> will emit the set of scanner, currently connected scanners, when one connects/disconnects
.onEach {
txtView.text = "Barcode scanners: ${it.size}\n"
}
.flatMapMerge { // Flatten to Flow<BarcodeScanner>
it.asFlow()
}
.onEach { scanner ->
txtView.append("${scanner.name()} [${scanner.hashCode()}]\n")
}
.collect { scanner ->
//if a new Set<BarcodeScanner> is emitted, this Flow<Barcode> returned by scanner.scan() will be subscribed again, without removing the old stream
//resulting in multiple results on a single scan.
scanner.scan() //returns an Flow<Barcode>
.onEach { barcode ->
findViewById<TextView>(R.id.txtViewBarcodes).append("[${scanner.name()}]: ${barcode.value} (${barcode.type.description})\n")
}
.onCompletion {
Timber.i("[${scanner.name()}]: DISCONNECTED") //Never called
}
.launchIn(scanScope)
}
}
I think rather than:
.collect { scanner ->
scanner.scan()
.onEach { barcode ->
...
You should use:
.flatMapLatest { scanner ->
scanner.scan()
}
.onEach { barcode ->
...
Where the flow is launched in the same UI scope. The problem you have is launchIn(scanScope) is creating a completely separate coroutine that the outer coroutine can't cancel when a new set of barcodes is emitted.

readLine() doesn't wait for user input in Kotlin/Native

Here is a simple script
fun main() {
print("ready> ")
val input = readLine()
println("User input: $input")
}
When I run this program with gradle runReleaseExecutableMacos I expect that I'll see a ready> prompt and will have a possibility to type some chars. But this program finishes immediately with User input: null as a result.
Am I missing something?
To achieve the behavior you desire, you can run the executable file produced by the Gradle. It will have an extension *.kexe.
Also, you can extend your build.gradle file with additional parameter. you got to add something like this:
macosX64("macos") {
binaries {
executable {
runTask.standardInput = System.in
}
}
}

Why am I getting a Java IO Exception in this very simple code?

The following is some sample code I created to get myself more familiar with Groovy. I have a good understanding of Java and I am trying to now learn this new language.
class Activity {
static void reverseString() {
def text
System.in.withReader{
println "Enter a string to be reversed:"
text = it.readLine()
}
print "\n";
for (int i = text.length() - 1; i >= 0; i--){
print text[i];
}
}
static void main(String[] args) {
def selection
System.in.withReader{
println "Select a project:"
println "1 - Reverse String"
selection = it.readLine()
}
switch (selection) {
case "1":
reverseString()
break
}
}
}
I am able to compile and run this code. I am able to enter '1' and press enter, and then the prompt from my method shows up. At this point I am supposed to enter a string to reverse, but before I can enter I get the IO Exception:
Exception in thread "main" java.io.IOException: Stream closed
at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:170)
at java.io.BufferedInputStream.read(BufferedInputStream.java:336)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:127)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:112)
at java.io.InputStreamReader.read(InputStreamReader.java:168)
at Activity$_reverseString_closure1.doCall(main.groovy:10)
at Activity.reverseString(main.groovy:7)
at Activity.main(main.groovy:39)
What am I missing here?
The purpose of withReader() is to ensure the stream is closed. So after the project selection is input in the main() method, the stream is closed. When reverseString() is executed, it's too late; the stream is closed.
Don't close System.in (directly, or through withReader) . Only close streams that your code creates, not streams that your application receives from a caller, or global instances in the runtime.

C++ pipe process not terminating

I made a program that takes two commands typed in by the user and pipes the first into the second. It works fine if real commands are entered but I'm having a problem getting my error check to work when I just enter random words. In most places of my code where I use exit() the process terminates fine but within the two child processes rs1 and rs2, I cannot exit. I'm new to processes so I'm not sure how to do this correctly. Can someone help?
int main()
{
while(true)
{
int argc1=0,argc2=0,rs1,rs2,pipefd[2];
char input1[80],input2[80],*argv1[6],*argv2[6],*p1,*p2;
//*Big block of code that receives input goes here*
pipe(pipefd);
rs1=fork();
rs2=fork();
if(rs1==0)
{
close(pipefd[0]);
close(1);
dup(pipefd[1]);
close(pipefd[1]);
rs1=execvp(argv1[0],argv1);
if(rs1==-1)
{
perror(argv1[0]);
exit(rs1); //Here is where I'm having the problem
}
}
if(rs2==0)
{
close(pipefd[1]);
close(0);
dup(pipefd[0]);
close(pipefd[0]);
rs2=execvp(argv2[0],argv2);
if(rs2==-1)
{
perror(argv2[0]);
exit(rs2); //Here also does not work correctly
}
}
close(pipefd[0]);
close(pipefd[1]);
wait(&rs1);
wait(&rs2);
}
return 0;
}