joeylib2/examples/joy/joy.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);
stagePresentRect(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;
}
stagePresent();
}
// 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 = 8 * 1024;
config.maxSurfaces = 4;
config.audioBytes = 64UL * 1024;
config.assetBytes = 128UL * 1024;
if (!joeyInit(&config)) {
fprintf(stderr, "joeyInit failed: %s\n", joeyLastError());
return 1;
}
screen = stageGet();
if (screen == NULL) {
fprintf(stderr, "stageGet 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;
}