Skip to content

Commit

Permalink
Fixed joystick emulation with keyboard: fast direction changing (left…
Browse files Browse the repository at this point in the history
…<->right or up<->down) didn't work properly.

Added fast save/load states.
Version 1.8.4
  • Loading branch information
abbruzze committed Sep 3, 2023
1 parent b122651 commit be72b39
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 51 deletions.
47 changes: 31 additions & 16 deletions Kernal64/src/ucesoft/cbm/CBMComputer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ abstract class CBMComputer extends CBMComponent {
protected var fullScreenAtBoot = false // used with --fullscreen
protected var ignoreConfig = false // used with --ignore-config-file

protected var lastSavedSnapshotFile = ""

protected val cartMenu = new JMenu("Cartridge")
protected var cartButtonRequested = false

Expand Down Expand Up @@ -572,12 +574,21 @@ abstract class CBMComputer extends CBMComponent {
protected def listBASIC(): Unit

protected def setStateMenu(stateMenu: JMenu): Unit = {
import java.awt.event.InputEvent._
val saveStateItem = new JMenuItem("Save state ...")
saveStateItem.addActionListener(_ => saveState())
stateMenu.add(saveStateItem)
val saveLastStateItem = new JMenuItem("Save on last state")
saveLastStateItem.addActionListener(_ => saveState(saveOnLast = true))
saveLastStateItem.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, ALT_DOWN_MASK | SHIFT_DOWN_MASK))
stateMenu.add(saveLastStateItem)
val loadStateItem = new JMenuItem("Load state ...")
loadStateItem.addActionListener(_ => loadState(None))
stateMenu.add(loadStateItem)
val loadLastStateItem = new JMenuItem("Load from last state")
loadLastStateItem.addActionListener(_ => loadState(if (lastSavedSnapshotFile.isEmpty) None else Some(lastSavedSnapshotFile) ))
loadLastStateItem.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, ALT_DOWN_MASK | SHIFT_DOWN_MASK))
stateMenu.add(loadLastStateItem)
}

protected def setTraceMenu(traceMenu: JMenu): Unit = {
Expand Down Expand Up @@ -700,7 +711,6 @@ abstract class CBMComputer extends CBMComponent {
try {
val canLoad = allowsState
if (!canLoad) {

showError("State saving error","Can't load state")
return
}
Expand Down Expand Up @@ -733,7 +743,7 @@ abstract class CBMComputer extends CBMComponent {
if (model != cbmModel) throw new IOException(s"Invalid state file. Found state saved for model ${model.modelName}, expected ${cbmModel.modelName}")
val ver = in.readObject.asInstanceOf[String]
val ts = in.readLong
if (!loadStateFromOptions && !asked) {
if (!loadStateFromOptions && !asked && fileName.isEmpty) {
val msg = s"<html><b>Version:</b> $ver<br><b>Date:</b> ${new java.util.Date(ts)}</html>"
JOptionPane.showConfirmDialog(displayFrame, msg, "State loading confirmation", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) match {
case JOptionPane.YES_OPTION =>
Expand Down Expand Up @@ -775,34 +785,39 @@ abstract class CBMComputer extends CBMComponent {
}
}

protected def saveState() : Unit = {
protected def saveState(saveOnLast:Boolean = false) : Unit = {
clock.pause()
var out : ObjectOutputStream = null
try {
val canSave = allowsState
if (!canSave) {

showError("State saving error","Can't save state")
return
}
val fc = new JFileChooser
fc.setDialogTitle("Choose where to save current state")
fc.setCurrentDirectory(new File(configuration.getProperty(CONFIGURATION_LASTDISKDIR,"./")))
fc.setFileFilter(new FileFilter {
def accept(f: File): Boolean = f.isDirectory || f.getName.toUpperCase.endsWith(".K64")
def getDescription = "Kernal64 state files"
})
val fn = fc.showSaveDialog(getActiveFrame.get) match {
case JFileChooser.APPROVE_OPTION =>
if (fc.getSelectedFile.getName.toUpperCase.endsWith(".K64")) fc.getSelectedFile.toString else fc.getSelectedFile.toString + ".k64"
case _ =>
return
val fn = if (saveOnLast && lastSavedSnapshotFile.nonEmpty) lastSavedSnapshotFile
else {
val fc = new JFileChooser
fc.setDialogTitle("Choose where to save current state")
fc.setCurrentDirectory(new File(configuration.getProperty(CONFIGURATION_LASTDISKDIR, "./")))
fc.setFileFilter(new FileFilter {
def accept(f: File): Boolean = f.isDirectory || f.getName.toUpperCase.endsWith(".K64")

def getDescription = "Kernal64 state files"
})
fc.showSaveDialog(getActiveFrame.get) match {
case JFileChooser.APPROVE_OPTION =>
if (fc.getSelectedFile.getName.toUpperCase.endsWith(".K64")) fc.getSelectedFile.toString else fc.getSelectedFile.toString + ".k64"
case _ =>
return
}
}

out = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(fn)))
out.writeObject(cbmModel)
out.writeObject(ucesoft.cbm.Version.VERSION)
out.writeLong(System.currentTimeMillis)
save(out)
lastSavedSnapshotFile = fn
out.close()
}
catch {
Expand Down
112 changes: 77 additions & 35 deletions Kernal64/src/ucesoft/cbm/peripheral/controlport/ControlPort.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ object ControlPort {
private abstract class AbstractControlPort extends ControlPort with MouseListener with KeyListener {
protected var mask = 0

protected def getKeyMask(e:KeyEvent) : Int
protected def applyKey(e:KeyEvent,keyPressed:Boolean) : Unit
def mouseClicked(e:MouseEvent) : Unit = {}
def mouseEntered(e:MouseEvent) : Unit = {}
def mouseExited(e:MouseEvent) : Unit = {}
Expand All @@ -86,18 +86,50 @@ object ControlPort {
else
if (!isLightPenEmulationEnabled) mask &= ~16
}
def keyPressed(e: KeyEvent): Unit = mask |= getKeyMask(e)
def keyReleased(e: KeyEvent): Unit = mask &= ~getKeyMask(e)

def keyPressed(e: KeyEvent): Unit = applyKey(e,keyPressed = true)

def keyReleased(e: KeyEvent): Unit = applyKey(e,keyPressed = false)
def keyTyped(e: KeyEvent) : Unit = {}

protected def read: Int = ~mask & 0xFF

protected def up(pressed:Boolean): Unit = {
if (pressed) {
mask |= 1
mask &= ~2
}
else mask &= ~1
}
protected def down(pressed: Boolean): Unit = {
if (pressed) {
mask &= ~1
mask |= 2
}
else mask &= ~2
}
protected def right(pressed: Boolean): Unit = {
if (pressed) {
mask &= ~4
mask |= 8
}
else mask &= ~8
}
protected def left(pressed: Boolean): Unit = {
if (pressed) {
mask &= ~8
mask |= 4
}
else mask &= ~4
}
protected def fire(pressed: Boolean): Unit = {
if (pressed) mask |= 16 else mask &= ~16
}
}

val emptyControlPort : ControlPort with MouseListener = new AbstractControlPort {
override val isConnected = false
protected def getKeyMask(e:KeyEvent) : Int = 0
protected def applyKey(e:KeyEvent,keyPressed:Boolean) : Unit = {}
override def mousePressed(e:MouseEvent): Unit = {
if (isMouse1351EmulationEnabled) super.mousePressed(e)
}
Expand All @@ -119,25 +151,25 @@ object ControlPort {
}
else false
}
protected def getKeyMask(e:KeyEvent): Int = {
protected def applyKey(e:KeyEvent,keyPressed:Boolean): Unit = {
import KeyEvent._
if (e.getKeyLocation == KEY_LOCATION_NUMPAD) {
if (e.getKeyCode == VK_CONTROL) fire(keyPressed)
else if (e.getKeyLocation == KEY_LOCATION_NUMPAD) {
e.getKeyCode match {
case VK_NUMPAD8|VK_UP if (mask & 2) == 0 => 1 // up
case VK_NUMPAD2|VK_DOWN if (mask & 1) == 0 => 2 // down
case VK_NUMPAD4|VK_LEFT if (mask & 8) == 0 => 4 // left
case VK_NUMPAD6|VK_RIGHT if (mask & 4) == 0 => 8 // right
case VK_NUMPAD9 => 9 // up+right
case VK_NUMPAD3 => 10// down+right
case VK_NUMPAD7 => 5 // up+left
case VK_NUMPAD1 => 6 // down+left
case VK_NUMPAD0|VK_INSERT => 16 // fire
case _ => 0
case VK_NUMPAD8|VK_UP => up(keyPressed)
case VK_NUMPAD2|VK_DOWN => down(keyPressed)
case VK_NUMPAD4|VK_LEFT => left(keyPressed)
case VK_NUMPAD6|VK_RIGHT => right(keyPressed)
case VK_NUMPAD9 => up(keyPressed) ; right(keyPressed)
case VK_NUMPAD3 => down(keyPressed) ; right(keyPressed)
case VK_NUMPAD7 => up(keyPressed) ; left(keyPressed)
case VK_NUMPAD1 => down(keyPressed) ; left(keyPressed)
case VK_NUMPAD0|VK_INSERT => fire(keyPressed)
case _ =>
}
}
else 0
}
}
}
}
/**
* Joystick emulation via user defined keys
*/
Expand All @@ -163,22 +195,32 @@ object ControlPort {
else false
}

protected def getKeyMask(e:KeyEvent): Int = {
protected def applyKey(e:KeyEvent,keyPressed:Boolean): Unit = {
import KeyEvent._
if (e.getKeyLocation != KEY_LOCATION_NUMPAD) {
val keyCode = e.getKeyCode
if (keyCode == upKey && (mask & 2) == 0) 1 // up
else if (keyCode == downKey && (mask & 1) == 0) 2 // down
else if (keyCode == leftKey && (mask & 8) == 0) 4 // left
else if (keyCode == rightKey && (mask & 4) == 0) 8 // right
else if (keyCode == upRightKey) 9 // up+right
else if (keyCode == downRightKey) 10// down+right
else if (keyCode == upLeftKey) 5 // up+left
else if (keyCode == downLeftKey) 6 // down+left
else if (keyCode == fireKey) 16// fire
else 0
if (keyCode == upKey) up(keyPressed)
else if (keyCode == downKey) down(keyPressed)
else if (keyCode == leftKey) left(keyPressed)
else if (keyCode == rightKey) right(keyPressed)
else if (keyCode == upRightKey) {
up(keyPressed)
right(keyPressed)
}
else if (keyCode == downRightKey) {
down(keyPressed)
right(keyPressed)
}
else if (keyCode == upLeftKey) {
up(keyPressed)
left(keyPressed)
}
else if (keyCode == downLeftKey) {
down(keyPressed)
left(keyPressed)
}
else if (keyCode == fireKey) fire(keyPressed)
}
else 0
}

override def updateConfiguration() : Unit = {
Expand Down

0 comments on commit be72b39

Please sign in to comment.