// 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 #include #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; }