Как обнаружить нажатия клавиш на консоли?

Я пишу roguelike на Scala. Мне нужно иметь возможность видеть, например, когда пользователь нажимает клавишу со стрелкой. Все решения, которые я нашел, требуют, чтобы игрок нажал клавишу ввода.

Есть ли способ обнаружить нажатия клавиш в консольном приложении аналогично getch() в C?


person Serentty    schedule 20.12.2011    source источник


Ответы (5)


Scala включает jline в свой дистрибутив (с немного измененной упаковкой), так как использует его в REPL. Обычно он не загружается, но если вы знаете, где находится ваш каталог библиотеки Scala, вы можете добавить его в путь к классам. Если вы добавите его в путь к классам, то это будет работать:

object Key extends App {
  val con = new tools.jline.console.ConsoleReader()
  println( con.readVirtualKey() )
}

(обратите внимание, что этот метод даст ^P ^N ^B ^F для перемещения вверх, вниз, влево и вправо соответственно, что соответствует стандартным клавишам управления). Но это также переопределит стандартное поведение System.in.read(), поэтому вы можете передать ему массивы длины 1, чтобы получить следующий ожидающий байт (и проверить наличие ожидающих байтов с помощью System.in.available, которое должно быть больше 1 для клавиш со стрелками).

person Rex Kerr    schedule 20.12.2011

Проблема с Enter заключается в том, что терминал буферизует ввод, поэтому вам нужно перевести его в режим raw. Обратите внимание, что это проблема конкретной платформы, поэтому она не очень переносима.

Посмотрите ответы на этот вопрос: Как прочитать один символ из консоли в Java (по мере того, как пользователь его вводит)?

Краткий ответ для систем UNIX:

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)
}
person Rogach    schedule 21.12.2011

Я должен что-то упустить:

System.in.read

Работает на меня.

person Jed Wesley-Smith    schedule 21.12.2011
comment
эээ, нет, это просто InputStream, read() читает один байт (как int). Если вы попробуете это на консоли, вы увидите, что на самом деле он будет считывать одно нажатие клавиши со стрелкой: scala > System.in.read res0: Int = 27 - person Jed Wesley-Smith; 05.05.2012

Scala находится поверх JVM, поэтому ответ для Scala точно такой же, как и для Java.

К сожалению, я не думаю, что вам это понравится - см. этот другой ответ для деталей.

person Paul Butcher    schedule 20.12.2011
comment
Могу ли я просто создать невидимое окно для записи нажатий клавиш? - person Serentty; 21.12.2011
comment
Вам действительно нужно, чтобы ваше приложение было консольным? То, что вы пытаетесь сделать, было бы намного проще в графическом интерфейсе (Swing или SWT - в зависимости от того, что вы предпочитаете). Я уверен, что вы могли бы создать графический интерфейс, который выглядит как консоль, если вы действительно хотите этого мошеннического ощущения? - person Paul Butcher; 21.12.2011
comment
Я предполагаю, что это могло быть приложением с графическим интерфейсом. Идея заключалась в том, что в нее можно будет играть через Telnet или что-то в этом роде. - person Serentty; 21.12.2011

Я использовал версию 3 jline. Это не работает внутри IntelliJ IDE, но отлично работает в SBT в терминале моего Macbook.

build.sbt

libraryDependencies += "org.jline" % "jline" % "3.1.3"

Это пример приложения, которое остановится при нажатии клавиши «Ввод».

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()

Это должно сработать.

person Felix    schedule 28.10.2018