// Visual keyboard + mouse demo: one square per JoeyKeyE, lit when its // key is held, and a small pointer drawn at the live mouse position. // Holding the left mouse button while the pointer is over a cell lights // it as if the corresponding key were down. Press ESC to quit. // // The render loop only redraws cells whose target lit state changed // since last frame -- so on idle frames, the only work is the cursor // erase + redraw + a tiny rect-present pair. The cursor erase is // implemented by redrawing the cell that contained the *previous* // cursor position; in the gap regions between cells the cursor will // briefly leave a trail until that cell is touched again, which is an // acceptable demo-quality compromise. #include #include #define GRID_COLS 10 #define GRID_ROWS 6 #define CELL_W 28 #define CELL_H 28 #define GAP 4 #define MARGIN_X 2 #define MARGIN_Y 6 #define CURSOR_W 4 #define CURSOR_H 4 #define COLOR_BACKGROUND 0 #define COLOR_UNLIT 1 #define COLOR_LIT 2 #define COLOR_CURSOR 3 #define CELL_NONE ((int16_t)-1) static void buildPalette(SurfaceT *screen); static void cellAtPoint(int16_t px, int16_t py, int16_t *outCol, int16_t *outRow); static bool cellTargetLit(int16_t col, int16_t row, int16_t cursorCol, int16_t cursorRow); static void drawCell(SurfaceT *screen, int16_t col, int16_t row, bool lit); static void drawCursor(SurfaceT *screen, int16_t x, int16_t y); static void initialPaint(SurfaceT *screen); static void presentChangedCells(SurfaceT *screen, int16_t cursorCol, int16_t cursorRow); static void updateCursor(SurfaceT *screen, int16_t cursorCol, int16_t cursorRow); // Keys laid out row-by-row. KEY_NONE cells stay blank. Shape roughly // resembles a real keyboard (top number row, then QWERTY rows, then a // cluster of modifiers / arrows / function keys). static const JoeyKeyE gKeyGrid[GRID_ROWS][GRID_COLS] = { { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0 }, { KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P }, { KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_BACKSPACE }, { KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_LSHIFT, KEY_RSHIFT, KEY_TAB }, { KEY_SPACE, KEY_ESCAPE, KEY_RETURN, KEY_LCTRL, KEY_LALT, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_NONE }, { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10 } }; static bool gCellLit[GRID_ROWS][GRID_COLS]; static int16_t gLastCursorX = -100; static int16_t gLastCursorY = -100; static int16_t gLastCursorCol = CELL_NONE; static int16_t gLastCursorRow = CELL_NONE; 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; // black colors[COLOR_UNLIT] = 0x0333; // dark gray colors[COLOR_LIT] = 0x00F0; // bright green colors[COLOR_CURSOR] = 0x0FFF; // white paletteSet(screen, 0, colors); } static void cellAtPoint(int16_t px, int16_t py, int16_t *outCol, int16_t *outRow) { int16_t col; int16_t row; int16_t cx; int16_t cy; *outCol = CELL_NONE; *outRow = CELL_NONE; for (row = 0; row < GRID_ROWS; row++) { for (col = 0; col < GRID_COLS; col++) { cx = (int16_t)(MARGIN_X + col * (CELL_W + GAP)); cy = (int16_t)(MARGIN_Y + row * (CELL_H + GAP)); if (px >= cx && px < (cx + CELL_W) && py >= cy && py < (cy + CELL_H)) { *outCol = col; *outRow = row; return; } } } } static bool cellTargetLit(int16_t col, int16_t row, int16_t cursorCol, int16_t cursorRow) { JoeyKeyE key; key = gKeyGrid[row][col]; if (key == KEY_NONE) { return false; } if (joeyKeyDown(key)) { return true; } if (col == cursorCol && row == cursorRow && joeyMouseDown(MOUSE_BUTTON_LEFT)) { return true; } return false; } static void drawCell(SurfaceT *screen, int16_t col, int16_t row, bool lit) { int16_t x; int16_t y; uint8_t color; x = (int16_t)(MARGIN_X + col * (CELL_W + GAP)); y = (int16_t)(MARGIN_Y + row * (CELL_H + GAP)); color = lit ? COLOR_LIT : COLOR_UNLIT; fillRect(screen, x, y, CELL_W, CELL_H, color); } static void drawCursor(SurfaceT *screen, int16_t x, int16_t y) { fillRect(screen, x, y, CURSOR_W, CURSOR_H, COLOR_CURSOR); } static void initialPaint(SurfaceT *screen) { int16_t col; int16_t row; JoeyKeyE key; surfaceClear(screen, COLOR_BACKGROUND); for (row = 0; row < GRID_ROWS; row++) { for (col = 0; col < GRID_COLS; col++) { key = gKeyGrid[row][col]; if (key == KEY_NONE) { continue; } drawCell(screen, col, row, false); gCellLit[row][col] = false; } } surfacePresent(screen); } static void presentChangedCells(SurfaceT *screen, int16_t cursorCol, int16_t cursorRow) { int16_t col; int16_t row; JoeyKeyE key; bool lit; int16_t x; int16_t y; for (row = 0; row < GRID_ROWS; row++) { for (col = 0; col < GRID_COLS; col++) { key = gKeyGrid[row][col]; if (key == KEY_NONE) { continue; } lit = cellTargetLit(col, row, cursorCol, cursorRow); if (lit == gCellLit[row][col]) { continue; } drawCell(screen, col, row, lit); x = (int16_t)(MARGIN_X + col * (CELL_W + GAP)); y = (int16_t)(MARGIN_Y + row * (CELL_H + GAP)); surfacePresentRect(screen, x, y, CELL_W, CELL_H); gCellLit[row][col] = lit; } } } // Erase the previous cursor (by redrawing the cell that held it) and // stamp the new cursor at the current mouse position. Both rects are // presented; if the cursor stayed inside the same cell only one rect // pair is touched, so steady-state cost is small. static void updateCursor(SurfaceT *screen, int16_t cursorCol, int16_t cursorRow) { int16_t mouseX; int16_t mouseY; mouseX = joeyMouseX(); mouseY = joeyMouseY(); if (gLastCursorX != mouseX || gLastCursorY != mouseY) { if (gLastCursorCol != CELL_NONE) { drawCell(screen, gLastCursorCol, gLastCursorRow, gCellLit[gLastCursorRow][gLastCursorCol]); surfacePresentRect(screen, (int16_t)(MARGIN_X + gLastCursorCol * (CELL_W + GAP)), (int16_t)(MARGIN_Y + gLastCursorRow * (CELL_H + GAP)), CELL_W, CELL_H); } else if (gLastCursorX >= 0 && gLastCursorY >= 0) { // Old cursor was in a gap region. Stamp background over it. fillRect(screen, gLastCursorX, gLastCursorY, CURSOR_W, CURSOR_H, COLOR_BACKGROUND); surfacePresentRect(screen, gLastCursorX, gLastCursorY, CURSOR_W, CURSOR_H); } } drawCursor(screen, mouseX, mouseY); surfacePresentRect(screen, mouseX, mouseY, CURSOR_W, CURSOR_H); gLastCursorX = mouseX; gLastCursorY = mouseY; gLastCursorCol = cursorCol; gLastCursorRow = cursorRow; } int main(void) { JoeyConfigT config; SurfaceT *screen; int16_t cursorCol; int16_t cursorRow; 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; } cellAtPoint(joeyMouseX(), joeyMouseY(), &cursorCol, &cursorRow); presentChangedCells(screen, cursorCol, cursorRow); updateCursor(screen, cursorCol, cursorRow); } joeyShutdown(); return 0; }