How to detect key presses on console? - scala

I'm writing a roguelike in Scala. I need to be able to see when the user presses an arrow key for example. All of the solutions I have found require the player to press enter.
Is there any way to detect keypresses in a console app in a similar manner to getch() in C?

The problem with Enter is that terminal buffers input, so you need to turn it to raw mode. Note that it is a platform-specific issue, so it is not very portable.
Look at answers to this question: How to read a single char from the console in Java (as the user types it)?
Short answer for UNIX systems:
import sys.process._
(Seq("sh", "-c", "stty -icanon min 1 < /dev/tty") !)
(Seq("sh", "-c", "stty -echo < /dev/tty") !)
(1 to 20).foreach { _ =>
val c = Console.in.read
println("Got " + c)
}

Scala includes jline in its distribution (with slightly modified packaging), since it uses it in the REPL. It's not normally loaded, but if you know where your Scala lib directory is, you can add it to the classpath. If you add it to the classpath, then this will work:
object Key extends App {
val con = new tools.jline.console.ConsoleReader()
println( con.readVirtualKey() )
}
(note that this method will give ^P ^N ^B ^F for up, down, left, and right, respectively, matching the standard control keys). But this will also override the standard behavior of System.in.read(), so you can pass it arrays of length 1 to get the next waiting byte (and test for waiting bytes with System.in.available, which should be more than 1 for arrow keys).

I must be missing something:
System.in.read
Works for me.

Scala sits on top of the JVM, so the answer for Scala is exactly the same as the answer for Java.
Unfortunately, I don't think you're going to like it - see this other answer for details.

I used the version 3 of jline. It doesn't work inside the IntelliJ IDE, but perfectly in SBT in the terminal of my Macbook.
build.sbt
libraryDependencies += "org.jline" % "jline" % "3.1.3"
This is an example app, which will stop on pressing the key 'enter'.
Main.scala
import org.jline.terminal.TerminalBuilder
object Main extends App {
val terminal = TerminalBuilder.builder().jna(true).system(true).build()
terminal.enterRawMode()
val reader = terminal.reader()
var input = -1
while (input != 13) {
input = reader.read
input match {
case 'a' =>
println("you pressed 'a'")
case unknown =>
println(s"you pressed a unknown key $unknown")
}
}
}
terminal.close()
reader.close()
That should work.

Related

In Appium Python, how to press key code by key name?

I am writing Python Appium code. The code needs to press the HOME button. Right now I have
driver.press_keycode(3)
where 3 is mapped to KEYCODE_HOME according to keycode mapping.
Is there a way that I can refer KEYCODE_HOME in my code so that my code is more readable?
I could have done the following:
adding a comment
# HOME Key is 3
driver.press_keycode(3)
or assigning a variable
home_keycode=3
driver.press_keycode(home_keycode)
But I'd more like to see something like
driver.press_keycode(AppiumKey.HOME)
Does such a thing exist?
Thank you
So you can create Enum class and call variables like Keys.HOME etc
class Keys(Enum):
"""
List of keys
"""
HOME = 3
class PO(Base):
press_key(Keys.HOME)
Or use an existing solution as well python-client nativekey.py
press_key(AndroidKey.HOME)
I use an example to substantiate Dmytro's answer
import appium.webdriver.extensions.android.nativekey as nativekey
androidkey = nativekey.AndroidKey
value = 'TAB' # use TAB key as an example
keycode = androidkey.__dict__.get(value, None)
print(f"key={value}, keycode={keycode}")
# driver init code is skipped
driver.press_keycode(keycode)

Unable to get IntelliJ's (Velocity) Code Template to work correctly with conditional variable assignment

I am attempting to improve a Scala Companion Object plus Case Class code generating template in IntelliJ 2021.3 (Build #IU-213.5744.223, built on November 27, 2021).
I would like to allow the user to be able to leave some of the input parameters unspecified (empty) and then have the template generate sane defaults. This is because the generated code can then be refactored by the client to something more pertinent later.
My question is this: Is there another way to approach achieving my goal than the way I show below using the Velocity #macro (which I got from here, and then doesn't appear to work anyway)?
With the current approach using the #marco, when I invoke it...
With:
NAME = "Longitude"
PROPERTY_1_VARIABLE = "value"
PROPERTY_1_TYPE - "Double"
The Scala code target:
final case class Longitude(value: Double)
With:
NAME = "Longitude"
PROPERTY_1_VARIABLE = ""
PROPERTY_1_TYPE - ""
The Scala code target using defaults for 2 and 3:
final case class Longitude(property1: Property1Type)
And here's my attempt to make it work with the IntelliJ (Velocity) Code Template:
#macro(condOp $check, $default)
#if ($check == "")
$default
#else
$check
#end
#end
#set (${PROPERTY_1_VARIABLE} = "#condOp(${PROPERTY_1_VARIABLE}, 'property1')")
#set (${PROPERTY_1_TYPE} = "#condOp(${PROPERTY_1_TYPE}, 'Property1Type')")
#if ((${PACKAGE_NAME} && ${PACKAGE_NAME} != ""))package ${PACKAGE_NAME} #end
final case class ${NAME} (${PROPERTY_1_VARIABLE}: ${PROPERTY_1_TYPE})
First, the IntelliJ popup box for entering the parameters correctly shows "Name:". And then incorrectly shows "default:" and "check". I have submitted a ticket with Jetbrains for this IntelliJ issue. Please vote for it, if you get a chance so it increases in priority.
And then when the macro finishes executing (I provide the Name of "LongLat", but leave the default and check parameters empty), I don't understand why I get the following gibberish.
package org.public_domain
${PROPERTY_1_TYPE})
Any guidance on fixing my current Velocity Code Template script or suggest another way to effectively approach solving the problem would be deeply appreciated.

Why sbt.Extracted remove the previously defined TaskKey while append method?

There is a suitable method in the sbt.Exctracted to add the TaskKey to the current state. Assume I have inState: State:
val key1 = TaskKey[String]("key1")
Project.extract(inState).append(Seq(key1 := "key1 value"), inState)
I have faced with the strange behavior when I do it twice. I got the exception in the following example:
val key1 = TaskKey[String]("key1")
val key2 = TaskKey[String]("key2")
val st1: State = Project.extract(inState).append(Seq(key1 := "key1 value"), inState)
val st2: State = Project.extract(st1).append(Seq(key2 := "key2 value"), st1)
Project.extract(st2).runTask(key1, st2)
leads to:
java.lang.RuntimeException: */*:key1 is undefined.
The question is - why does it work like this? Is it possible to add several TaskKeys while executing the particular task by several calls to sbt.Extracted.append?
The example sbt project is sbt.Extracted append-example, to reproduce the issue just run sbt fooCmd
Josh Suereth posted the answer to sbt-dev mail list. Quote:
The append function is pretty dirty/low-level. This is probably a bug in its implementation (or the lack of documentation), but it blows away any other appended setting when used.
What you want to do, (I think) is append into the current "Session" so things will stick around and the user can remove what you've done via "sesison clear" command.
Additonally, the settings you're passing are in "raw" or "fully qualified" form. If you'd for the setting you write to work exactly the same as it would from a build.sbt file, you need to transform it first, so the Scopes match the current project, etc.
We provide a utility in sbt-server that makes it a bit easier to append settings into the current session:
https://github.com/sbt/sbt-remote-control/blob/master/server/src/main/scala/sbt/server/SettingUtil.scala#L11-L29
I have tested the proposed solution and that works like a charm.

Start external console application from Scala in interactive mode

I am using scala.sys.process to start an external console application from within my Scala code. However, I hit a road block when the console app requires the user input.
Basically, when I start the console app with
Seq("powershell" , "myConsoleApp.exe").run
myConsoleApp.exe will not be started in its own "window". I can see the console app is running when I check the Task Manager. Without an actual window, I can't really key in anything.
I know Scala can return the program output to a String or a Stream[String] - I guess Scala will probably be able pipe input to the external process also.
But I really don't want to re-write such logic in Scala when all of them are already available in the external program.
I am wondering if there is a way to start an external console program in its own window? Or is this a shortcoming with scala.sys.process.
Thanks,
Adapted from this Java answer Show the CMD window with Java
import scala.sys.process._
Seq("cmd", "/c", "start", "PowerShell.exe", "myConsoleApp.exe") run
After some more googling, I found that my problem is more in the way I call powershell.
Here is a solution that works for me:
Seq("powershell", "Start-Process", "myConsoleApp.exe")
This will run interactively from Scala console, copy and :paste
val con = System.console
new java.lang.Thread() {
val in = new java.lang.Thread() {
override def run() {
while (true) {
Thread.sleep(1)
if (con.reader.ready)
con.reader.read()
}
}
}
override def run() {
in.start()
while (true) {
Thread.sleep(1000)
con.printf("\nHai")
}
}
}.start()

Pyglet, how to make the ESCAPE key not close the window?

I am writing a small sample program and I would like to override the default pyglet's behavioyr of ESC closing the app. I have something to the extent of:
window = pyglet.window.Window()
#window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.ESCAPE:
pass
but that does not seem to work.
I know the question is old, but just in case. You've got to return pyglet.event.EVENT_HANDLED to prevent default behaviour. I didn't test it, but in theory this should work:
#window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.ESCAPE:
return pyglet.event.EVENT_HANDLED
Same for me. The question is old, but I've found out that you should use window handlers mechanisms, thus making the current event not to propagate further.
You can prevent the remaining event
handlers in the stack from receiving
the event by returning a true value.
The following event handler, when
pushed onto the window, will prevent
the escape key from exiting the
program:
def on_key_press(symbol, modifiers):
if symbol == key.ESCAPE:
return True
window.push_handlers(on_key_press)
Here is that link
On the Google group for pyglet-users it is suggest could overload the window.Window.on_key_press(), although there are no code example of it.
It's simple actually, subclass Window and overide the on_key_press, like this:
class MyWindow(pyglet.window.Window):
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE:
return pyglet.event.EVENT_HANDLED