252 lines
7.2 KiB
C
252 lines
7.2 KiB
C
// Joystick demo: shows position and button state for both joystick
|
|
// ports as live, redraw-on-change visualizations. Each stick gets a
|
|
// frame with a dot indicating the current axis values, plus two
|
|
// button indicators below. A small status patch shows whether each
|
|
// port reports a connected stick. Press ESC to quit.
|
|
//
|
|
// Like the keys demo, the loop only redraws cells that changed since
|
|
// last frame so the per-cell rect-present cost stays small.
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <joey/joey.h>
|
|
|
|
#define COLOR_BACKGROUND 0
|
|
#define COLOR_FRAME 1
|
|
#define COLOR_DOT_OFF 2
|
|
#define COLOR_DOT_ON 3
|
|
#define COLOR_BUTTON_OFF 4
|
|
#define COLOR_BUTTON_ON 5
|
|
#define COLOR_CONNECTED 6
|
|
#define COLOR_DISCONNECTED 7
|
|
|
|
#define FRAME_SIZE 80
|
|
#define FRAME_INSET 2
|
|
#define DOT_SIZE 8
|
|
#define DOT_TRAVEL ((FRAME_SIZE - DOT_SIZE) / 2)
|
|
|
|
#define BUTTON_W 24
|
|
#define BUTTON_H 16
|
|
#define BUTTON_GAP 4
|
|
|
|
#define STATUS_W 80
|
|
#define STATUS_H 4
|
|
|
|
#define STICK_TOP 16
|
|
#define BUTTON_TOP (STICK_TOP + FRAME_SIZE + 6)
|
|
#define STATUS_TOP (BUTTON_TOP + BUTTON_H + 6)
|
|
|
|
#define STICK0_LEFT 24
|
|
#define STICK1_LEFT 216
|
|
|
|
static void buildPalette(SurfaceT *screen);
|
|
static void drawFrame(SurfaceT *screen, int16_t left);
|
|
static void drawAndPresent(SurfaceT *screen, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color);
|
|
static int16_t dotXFor(int16_t left, int8_t ax);
|
|
static int16_t dotYFor(int8_t ay);
|
|
static int16_t buttonXFor(int16_t left, int idx);
|
|
static void initialPaint(SurfaceT *screen);
|
|
static void updateStick(SurfaceT *screen, JoeyJoystickE js, int16_t left);
|
|
|
|
typedef struct {
|
|
int16_t dotX;
|
|
int16_t dotY;
|
|
bool btn[JOY_BUTTON_COUNT];
|
|
bool connected;
|
|
bool valid;
|
|
} StickViewT;
|
|
|
|
static StickViewT gView[JOYSTICK_COUNT];
|
|
|
|
|
|
static void buildPalette(SurfaceT *screen) {
|
|
uint16_t colors[SURFACE_COLORS_PER_PALETTE];
|
|
uint16_t i;
|
|
|
|
for (i = 0; i < SURFACE_COLORS_PER_PALETTE; i++) {
|
|
colors[i] = 0x0000;
|
|
}
|
|
colors[COLOR_BACKGROUND] = 0x0000;
|
|
colors[COLOR_FRAME] = 0x0444;
|
|
colors[COLOR_DOT_OFF] = 0x0666;
|
|
colors[COLOR_DOT_ON] = 0x00F0; // green
|
|
colors[COLOR_BUTTON_OFF] = 0x0333;
|
|
colors[COLOR_BUTTON_ON] = 0x0FF0; // yellow
|
|
colors[COLOR_CONNECTED] = 0x00F0;
|
|
colors[COLOR_DISCONNECTED] = 0x0F00; // red
|
|
|
|
paletteSet(screen, 0, colors);
|
|
}
|
|
|
|
|
|
static void drawAndPresent(SurfaceT *screen, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color) {
|
|
fillRect(screen, x, y, (uint16_t)w, (uint16_t)h, color);
|
|
surfacePresentRect(screen, x, y, (uint16_t)w, (uint16_t)h);
|
|
}
|
|
|
|
|
|
// Draw the static stick frame (just an outlined square -- four edges
|
|
// with the inset filled background).
|
|
static void drawFrame(SurfaceT *screen, int16_t left) {
|
|
int16_t top;
|
|
|
|
top = STICK_TOP;
|
|
fillRect(screen, left, top, FRAME_SIZE, FRAME_SIZE, COLOR_FRAME);
|
|
fillRect(screen,
|
|
(int16_t)(left + FRAME_INSET),
|
|
(int16_t)(top + FRAME_INSET),
|
|
(uint16_t)(FRAME_SIZE - 2 * FRAME_INSET),
|
|
(uint16_t)(FRAME_SIZE - 2 * FRAME_INSET),
|
|
COLOR_BACKGROUND);
|
|
}
|
|
|
|
|
|
static int16_t dotXFor(int16_t left, int8_t ax) {
|
|
int32_t cx;
|
|
int32_t off;
|
|
|
|
cx = left + FRAME_SIZE / 2 - DOT_SIZE / 2;
|
|
off = ((int32_t)ax * DOT_TRAVEL) / JOYSTICK_AXIS_MAX;
|
|
return (int16_t)(cx + off);
|
|
}
|
|
|
|
|
|
static int16_t dotYFor(int8_t ay) {
|
|
int32_t cy;
|
|
int32_t off;
|
|
|
|
cy = STICK_TOP + FRAME_SIZE / 2 - DOT_SIZE / 2;
|
|
off = ((int32_t)ay * DOT_TRAVEL) / JOYSTICK_AXIS_MAX;
|
|
return (int16_t)(cy + off);
|
|
}
|
|
|
|
|
|
static int16_t buttonXFor(int16_t left, int idx) {
|
|
int16_t centerX;
|
|
|
|
centerX = (int16_t)(left + FRAME_SIZE / 2);
|
|
if (idx == 0) {
|
|
return (int16_t)(centerX - BUTTON_W - BUTTON_GAP / 2);
|
|
}
|
|
return (int16_t)(centerX + BUTTON_GAP / 2);
|
|
}
|
|
|
|
|
|
static void initialPaint(SurfaceT *screen) {
|
|
int16_t i;
|
|
|
|
surfaceClear(screen, COLOR_BACKGROUND);
|
|
for (i = 0; i < JOYSTICK_COUNT; i++) {
|
|
int16_t left;
|
|
left = (i == 0) ? STICK0_LEFT : STICK1_LEFT;
|
|
drawFrame(screen, left);
|
|
gView[i].valid = false;
|
|
gView[i].connected = false;
|
|
}
|
|
surfacePresent(screen);
|
|
}
|
|
|
|
|
|
// Compare current joystick state against gView and redraw / present
|
|
// only the visual elements that changed.
|
|
static void updateStick(SurfaceT *screen, JoeyJoystickE js, int16_t left) {
|
|
StickViewT *v;
|
|
int8_t ax;
|
|
int8_t ay;
|
|
int16_t newDotX;
|
|
int16_t newDotY;
|
|
bool newBtn[JOY_BUTTON_COUNT];
|
|
bool connected;
|
|
int16_t i;
|
|
int16_t bx;
|
|
int16_t by;
|
|
|
|
v = &gView[js];
|
|
connected = joeyJoystickConnected(js);
|
|
ax = joeyJoystickX(js);
|
|
ay = joeyJoystickY(js);
|
|
newDotX = dotXFor(left, ax);
|
|
newDotY = dotYFor(ay);
|
|
for (i = 0; i < JOY_BUTTON_COUNT; i++) {
|
|
newBtn[i] = joeyJoyDown(js, (JoeyJoyButtonE)i);
|
|
}
|
|
|
|
if (!v->valid || v->connected != connected) {
|
|
drawAndPresent(screen,
|
|
(int16_t)(left + (FRAME_SIZE - STATUS_W) / 2),
|
|
STATUS_TOP,
|
|
STATUS_W, STATUS_H,
|
|
connected ? COLOR_CONNECTED : COLOR_DISCONNECTED);
|
|
v->connected = connected;
|
|
}
|
|
|
|
// Move the position dot: erase the old square (paint background),
|
|
// then stamp the new one. If position didn't change, skip.
|
|
if (!v->valid || v->dotX != newDotX || v->dotY != newDotY) {
|
|
if (v->valid) {
|
|
drawAndPresent(screen, v->dotX, v->dotY, DOT_SIZE, DOT_SIZE, COLOR_BACKGROUND);
|
|
}
|
|
drawAndPresent(screen, newDotX, newDotY, DOT_SIZE, DOT_SIZE,
|
|
(ax != 0 || ay != 0) ? COLOR_DOT_ON : COLOR_DOT_OFF);
|
|
v->dotX = newDotX;
|
|
v->dotY = newDotY;
|
|
} else if (!v->valid) {
|
|
// First paint of a centered dot: still need to draw it once.
|
|
drawAndPresent(screen, newDotX, newDotY, DOT_SIZE, DOT_SIZE, COLOR_DOT_OFF);
|
|
}
|
|
|
|
// Button squares.
|
|
by = BUTTON_TOP;
|
|
for (i = 0; i < JOY_BUTTON_COUNT; i++) {
|
|
if (v->valid && v->btn[i] == newBtn[i]) {
|
|
continue;
|
|
}
|
|
bx = buttonXFor(left, i);
|
|
drawAndPresent(screen, bx, by, BUTTON_W, BUTTON_H,
|
|
newBtn[i] ? COLOR_BUTTON_ON : COLOR_BUTTON_OFF);
|
|
v->btn[i] = newBtn[i];
|
|
}
|
|
|
|
v->valid = true;
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
JoeyConfigT config;
|
|
SurfaceT *screen;
|
|
|
|
config.hostMode = HOST_MODE_TAKEOVER;
|
|
config.codegenBytes = 32 * 1024;
|
|
config.maxSurfaces = 4;
|
|
config.audioBytes = 64 * 1024;
|
|
config.assetBytes = 128 * 1024;
|
|
|
|
if (!joeyInit(&config)) {
|
|
fprintf(stderr, "joeyInit failed: %s\n", joeyLastError());
|
|
return 1;
|
|
}
|
|
|
|
screen = surfaceGetScreen();
|
|
if (screen == NULL) {
|
|
fprintf(stderr, "surfaceGetScreen returned NULL\n");
|
|
joeyShutdown();
|
|
return 1;
|
|
}
|
|
|
|
buildPalette(screen);
|
|
scbSetRange(screen, 0, SURFACE_HEIGHT - 1, 0);
|
|
initialPaint(screen);
|
|
joeyInputPoll();
|
|
|
|
for (;;) {
|
|
joeyInputPoll();
|
|
if (joeyKeyPressed(KEY_ESCAPE)) {
|
|
break;
|
|
}
|
|
updateStick(screen, JOYSTICK_0, STICK0_LEFT);
|
|
updateStick(screen, JOYSTICK_1, STICK1_LEFT);
|
|
}
|
|
|
|
joeyShutdown();
|
|
return 0;
|
|
}
|