Skip to content

Commit

Permalink
[EXPERIMENTAL] Fully rework input handling again.
Browse files Browse the repository at this point in the history
This time, no dedicated runner to handle inputs, as this caused
issues whenever the input code was in a separate core from the
game logic, breaking games like Ratatouille and Heroes Lore: Wind
of Soltia. Instead, run it on the main FreeJ2ME thread and run
checks to see if the input code has to do something or not.

Given how far-reaching this is, i won't be surprised if it breaks
other stuff. So i'll be marking it as experimental.

Fixes #7 and #17, though i didn't test all versions of Ratatouille
  • Loading branch information
AShiningRay committed Dec 25, 2024
1 parent 7954a76 commit fb47eb4
Showing 9 changed files with 292 additions and 530 deletions.
108 changes: 50 additions & 58 deletions src/javax/microedition/lcdui/Canvas.java
Original file line number Diff line number Diff line change
@@ -54,10 +54,6 @@ public abstract class Canvas extends Displayable
private boolean fullscreen = false;
private boolean isPainting = false;

// Those are only used to discern actual soft key command bar inputs below
private boolean leftSoftPressed = false;
private boolean rightSoftPressed = false;

protected Canvas()
{
Mobile.log(Mobile.LOG_INFO, Canvas.class.getPackage().getName() + "." + Canvas.class.getSimpleName() + ": " + "Create Canvas:"+width+", "+height);
@@ -71,14 +67,14 @@ public int getGameAction(int keyCode)

// We should send those soft keys to handle commands if not fullscreen. As it means the command bar is visible
if(castKey == KEY_SOFT_LEFT && !fullscreen)
{
if(!leftSoftPressed) { keyPressedCommands(castKey); } // Make sure keyReleases aren't registered here
leftSoftPressed = !leftSoftPressed;
{
// This pressedKeys array position is commonized on all frontends, it always means the soft left key
if(MobilePlatform.pressedKeys[9]) { keyPressedCommands(castKey); } // Make sure keyReleases aren't registered here
}
else if(castKey == KEY_SOFT_RIGHT && !fullscreen)
{
if(!rightSoftPressed) { keyPressedCommands(castKey); }
rightSoftPressed = !rightSoftPressed;
// Same here, this array position always means the right soft key
if(MobilePlatform.pressedKeys[8]) { keyPressedCommands(castKey); }
}

return castKey;
@@ -88,26 +84,26 @@ public int getKeyCode(int gameAction)
{
switch(gameAction) // Look on Mobile.java for what these magic numbers mean ("J2ME Canvas standard keycodes")
{
case Mobile.KEY_NUM2: return Mobile.getMobileKey(14, true);
case Mobile.KEY_NUM8: return Mobile.getMobileKey(17, true);
case Mobile.KEY_NUM4: return Mobile.getMobileKey(15, true);
case Mobile.KEY_NUM6: return Mobile.getMobileKey(16, true);
case Mobile.KEY_NUM5: return Mobile.getMobileKey(18, true);
case Mobile.GAME_UP: return Mobile.getMobileKey(0, true);
case Mobile.GAME_DOWN: return Mobile.getMobileKey(1, true);
case Mobile.GAME_LEFT: return Mobile.getMobileKey(2, true);
case Mobile.GAME_RIGHT: return Mobile.getMobileKey(3, true);
case Mobile.GAME_FIRE: return Mobile.getMobileKey(7, true);
case Mobile.KEY_NUM2: return Mobile.getMobileKey(14);
case Mobile.KEY_NUM8: return Mobile.getMobileKey(17);
case Mobile.KEY_NUM4: return Mobile.getMobileKey(15);
case Mobile.KEY_NUM6: return Mobile.getMobileKey(16);
case Mobile.KEY_NUM5: return Mobile.getMobileKey(18);
case Mobile.GAME_UP: return Mobile.getMobileKey(0);
case Mobile.GAME_DOWN: return Mobile.getMobileKey(1);
case Mobile.GAME_LEFT: return Mobile.getMobileKey(2);
case Mobile.GAME_RIGHT: return Mobile.getMobileKey(3);
case Mobile.GAME_FIRE: return Mobile.getMobileKey(7);

// GAME_A through D don't show up in documentation at all.
case Mobile.GAME_A: case Mobile.KEY_NUM1: return Mobile.getMobileKey(10, true);
case Mobile.GAME_B: case Mobile.KEY_NUM3: return Mobile.getMobileKey(11, true);
case Mobile.GAME_C: case Mobile.KEY_NUM7: return Mobile.getMobileKey(5, true);
case Mobile.GAME_D: case Mobile.KEY_NUM9: return Mobile.getMobileKey(4, true);

case Mobile.KEY_NUM0: return Mobile.getMobileKey(6, true);
case Mobile.KEY_STAR: return Mobile.getMobileKey(12, true);
case Mobile.KEY_POUND: return Mobile.getMobileKey(13, true);
case Mobile.GAME_A: case Mobile.KEY_NUM1: return Mobile.getMobileKey(10);
case Mobile.GAME_B: case Mobile.KEY_NUM3: return Mobile.getMobileKey(11);
case Mobile.GAME_C: case Mobile.KEY_NUM7: return Mobile.getMobileKey(5);
case Mobile.GAME_D: case Mobile.KEY_NUM9: return Mobile.getMobileKey(4);

case Mobile.KEY_NUM0: return Mobile.getMobileKey(6);
case Mobile.KEY_STAR: return Mobile.getMobileKey(12);
case Mobile.KEY_POUND: return Mobile.getMobileKey(13);
}
return 0;
}
@@ -170,44 +166,40 @@ public void pointerReleased(int x, int y) { }

public void repaint(int x, int y, int width, int height)
{
Display.LCDUILock.lock();
try
{
try
if (getDisplay().getCurrent() != this || listCommands) { return; }

// TODO: This might be an issue
if (isPainting)
{
if (getDisplay().getCurrent() != this || listCommands) { return; }

// TODO: This might be an issue
if (isPainting)
{
// we need this to avoid stackoverflow
// but it seems the underlying problem is that when paint calls
// repaint, we shouldn't even land here...
Mobile.getDisplay().callSerially(() -> { repaint(x, y, width, height); });
return;
}

graphics.reset();
isPainting = true;

try { paint(graphics); }
catch (Exception e)
{
Mobile.log(Mobile.LOG_WARNING, Canvas.class.getPackage().getName() + "." + Canvas.class.getSimpleName() + ": " + "Exception hit in paint(graphics)" + e.getMessage());
}
finally { isPainting = false; }

// Draw command bar whenever the canvas is not fullscreen and there are commands in the bar
if (!fullscreen && !commands.isEmpty()) { paintCommandsBar(); }

Mobile.getPlatform().flushGraphics(platformImage, x, y, width, height);
// we need this to avoid stackoverflow
// but it seems the underlying problem is that when paint calls
// repaint, we shouldn't even land here...
Mobile.getDisplay().callSerially(() -> { repaint(x, y, width, height); });
return;
}

graphics.reset();
isPainting = true;

try { paint(graphics); }
catch (Exception e)
{
Mobile.log(Mobile.LOG_ERROR, Canvas.class.getPackage().getName() + "." + Canvas.class.getSimpleName() + ": " + "Serious Exception hit in repaint()" + e.getMessage());
e.printStackTrace();
Mobile.log(Mobile.LOG_WARNING, Canvas.class.getPackage().getName() + "." + Canvas.class.getSimpleName() + ": " + "Exception hit in paint(graphics)" + e.getMessage());
}
} finally { Display.LCDUILock.unlock(); }
finally { isPainting = false; }

// Draw command bar whenever the canvas is not fullscreen and there are commands in the bar
if (!fullscreen && !commands.isEmpty()) { paintCommandsBar(); }

Mobile.getPlatform().flushGraphics(platformImage, x, y, width, height);
}
catch (Exception e)
{
Mobile.log(Mobile.LOG_ERROR, Canvas.class.getPackage().getName() + "." + Canvas.class.getSimpleName() + ": " + "Serious Exception hit in repaint()" + e.getMessage());
e.printStackTrace();
}
}

public void serviceRepaints()
34 changes: 6 additions & 28 deletions src/javax/microedition/lcdui/Display.java
Original file line number Diff line number Diff line change
@@ -23,22 +23,14 @@
import javax.microedition.lcdui.Image;

import java.util.Vector;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Timer;
import java.util.TimerTask;

import java.util.concurrent.locks.ReentrantLock;

import org.recompile.mobile.Mobile;

public class Display
{
// when fps is limited, this needs to be a fair lock
// otherwise key events might not get dispatched at proper time
public static final ReentrantLock LCDUILock = new ReentrantLock(true);

// this can only be used from a dedicated thread which MAY NOT hold lcduilock
public static final Object calloutLock = new Object();

public static final int LIST_ELEMENT = 1;
public static final int CHOICE_GROUP_ELEMENT = 2;
public static final int ALERT = 3;
@@ -77,10 +69,9 @@ public Display()

public void callSerially(Runnable r)
{
LCDUILock.lock();
try { serialCalls.add(r); }
finally { LCDUILock.unlock(); }
serialCalls.add(r);
}

private class SerialCallTimerTask extends TimerTask
{
public void run()
@@ -89,16 +80,8 @@ public void run()
{
try
{
synchronized (calloutLock)
{
LCDUILock.lock();
try
{
Runnable call = serialCalls.get(0);
serialCalls.removeElementAt(0);
call.run();
} finally { LCDUILock.unlock(); }
}
serialCalls.get(0).run();
serialCalls.removeElement(0);
}
catch (Exception e) { }
}
@@ -170,7 +153,6 @@ public void setCurrent(Displayable next)
{
if (next == null) { return; }

LCDUILock.lock();
try
{
if(current == next || isSettingCurrent) { return; }
@@ -196,11 +178,7 @@ public void setCurrent(Displayable next)
e.printStackTrace();
}
}
finally
{
LCDUILock.unlock();
Mobile.displayUpdated = true;
}
finally { Mobile.displayUpdated = true; }
}

public void setCurrent(Alert alert, Displayable next)
74 changes: 37 additions & 37 deletions src/javax/microedition/lcdui/Displayable.java
Original file line number Diff line number Diff line change
@@ -277,31 +277,34 @@ protected void render()

protected void keyPressedCommands(int key)
{
if(key == Canvas.KEY_NUM2 || key == Canvas.UP)
if(!isValidating)
{
currentCommand--;
if(currentCommand<0) { currentCommand = commands.size()-1; }
}
else if(key == Canvas.KEY_NUM8 || key == Canvas.DOWN)
{
currentCommand++;
if(currentCommand>=commands.size()) { currentCommand = 0; }
}
else if (key == Canvas.KEY_NUM5 || key == Canvas.FIRE || key == Canvas.KEY_SOFT_LEFT)
{
doLeftCommand();
currentCommand = 0;
listCommands = false;
}
else if (key == Canvas.KEY_SOFT_RIGHT)
{
listCommands = false;
doRightCommand();
currentCommand = 0;
}
else { return; }
if(key == Canvas.KEY_NUM2 || key == Canvas.UP)
{
currentCommand--;
if(currentCommand<0) { currentCommand = commands.size()-1; }
}
else if(key == Canvas.KEY_NUM8 || key == Canvas.DOWN)
{
currentCommand++;
if(currentCommand>=commands.size()) { currentCommand = 0; }
}
else if (key == Canvas.KEY_NUM5 || key == Canvas.FIRE || key == Canvas.KEY_SOFT_LEFT)
{
doLeftCommand();
currentCommand = 0;
listCommands = false;
}
else if (key == Canvas.KEY_SOFT_RIGHT)
{
listCommands = false;
doRightCommand();
currentCommand = 0;
}
else { return; }

_invalidate();
_invalidate();
}
}

protected void doCommand(int index)
@@ -348,22 +351,19 @@ protected void _invalidate()
{
// zb3: TODO: consider queuing this
// the code below ensures this function is not reentrant
synchronized (Display.LCDUILock)
{
if (getDisplay().getCurrent() != this) { return; }
if (getDisplay().getCurrent() != this) { return; }

if (isValidating)
{
Mobile.log(Mobile.LOG_ERROR, Displayable.class.getPackage().getName() + "." + Displayable.class.getSimpleName() + ": " + "Recursive invalidation attempt detected.");
Thread.dumpStack();
}
else
{
isValidating = true;
if (isValidating)
{
Mobile.log(Mobile.LOG_ERROR, Displayable.class.getPackage().getName() + "." + Displayable.class.getSimpleName() + ": " + "Recursive invalidation attempt detected.");
Thread.dumpStack();
}
else
{
isValidating = true;

try { render(); }
finally { isValidating = false; }
}
try { render(); }
finally { isValidating = false; }
}
}
}
21 changes: 0 additions & 21 deletions src/org/recompile/freej2me/AWTGUI.java
Original file line number Diff line number Diff line change
@@ -108,27 +108,6 @@ public final class AWTGUI
new Button("R"),
};

/* Constant fields for key mapping indices (matches the array above) */
static final byte SOFT_LEFT_KEY = 0;
static final byte SOFT_RIGHT_KEY = 1;
static final byte UP_ARROW_KEY = 2;
static final byte LEFT_ARROW_KEY = 3;
static final byte OK_KEY = 4;
static final byte RIGHT_ARROW_KEY = 5;
static final byte DOWN_ARROW_KEY = 6;
static final byte NUMPAD1_KEY = 7;
static final byte NUMPAD2_KEY = 8;
static final byte NUMPAD3_KEY = 9;
static final byte NUMPAD4_KEY = 10;
static final byte NUMPAD5_KEY = 11;
static final byte NUMPAD6_KEY = 12;
static final byte NUMPAD7_KEY = 13;
static final byte NUMPAD8_KEY = 14;
static final byte NUMPAD9_KEY = 15;
static final byte NUMPAD_ASTERISK_KEY = 16;
static final byte NUMPAD0_KEY = 17;
static final byte NUMPAD_POUND_KEY = 18;

/* Array of inputs in order to support input remapping */
int inputKeycodes[] = new int[] {
KeyEvent.VK_Q, KeyEvent.VK_W,
Loading

0 comments on commit fb47eb4

Please sign in to comment.