// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License. // See LICENSE.md for license information. // SPDX-License-Identifier: MIT // // keysCells.c - read which of KEYS's named-key cells are lit off a // screenshot. // // keysCells PNG [--at X,Y] [--scale S] // // KEYS (examples/keys) draws a cell for each named key, lit (color 2, // green) while jlKeyDown says it is down and dark gray (color 1) // otherwise. This reads the cells of its fourth row that a port gives a // second physical key for -- Return, Ctrl, Alt, Up, Down, Left, and Right // -- at the middle of each, and prints one digit a cell, in that order: 1 // lit, 0 not, ? neither color. // // --at: where the stage's top left is in the screenshot (default 0,0). // --scale: screen pixels a stage pixel (default 1). A point off the // screenshot is fatal (exit 1); a bad command line exits 2. #include #include #include "gateArg.h" #include "gateImage.h" #include "toolFile.h" #define TOOL_NAME "keysCells" // examples/keys: CELL_W x CELL_H cells GAP apart from (MARGIN_X, // MARGIN_Y), the named keys in row 4 at these columns. #define CELL_W 28 #define CELL_H 26 #define GAP 4 #define MARGIN_X 2 #define MARGIN_Y 2 #define ROW 4 #define COLUMN_COUNT 7u // KEYS's colors 1 and 2, and how near a pixel must be: a 15- or 16-bit // screen is off by at most 8 a gun. #define UNLIT_INDEX 1u #define LIT_INDEX 2u #define MAX_DIST (3u * 9u * 9u) // Return, Ctrl, Alt, Up, Down, Left, Right. static const long kColumns[COLUMN_COUNT] = { 2, 3, 4, 5, 6, 7, 8 }; static const char *const kUsage[] = { "keysCells - read which of KEYS's named-key cells are lit off a screenshot.", "", "KEYS (examples/keys) draws a cell for each named key, lit (color 2, green)", "while jlKeyDown says it is down and dark gray (color 1) otherwise. This", "reads the cells of its fourth row that a port gives a second physical key", "for -- Return, Ctrl, Alt, Up, Down, Left, and Right -- at the middle of", "each, and prints one digit a cell, in that order: 1 lit, 0 not, ? neither", "color.", "", " keysCells PNG [--at X,Y] [--scale S]", "", "--at: where the stage's top left is in the screenshot (default 0,0).", "--scale: screen pixels a stage pixel (default 1)." }; int main(int argc, char **argv) { GateImageT img; const char *png; const uint8_t *pixel; uint16_t words[GATE_KEYS_COLORS]; uint8_t lit[GATE_RGB_BYTES]; uint8_t unlit[GATE_RGB_BYTES]; char digits[COLUMN_COUNT + 1u]; long atX; long atY; long scale; long x; long y; uint32_t i; int arg; toolFileName(TOOL_NAME); png = NULL; atX = 0; atY = 0; scale = 1; for (arg = 1; arg < argc; arg++) { if (strcmp(argv[arg], "--at") == 0 && arg + 1 < argc) { gateArgPair("at", argv[++arg], ',', &atX, &atY); } else if (strcmp(argv[arg], "--scale") == 0 && arg + 1 < argc) { scale = gateArgLong("scale", argv[++arg]); if (scale < 1) { gateArgBad("scale", argv[arg]); } } else if (argv[arg][0] == '-' || png != NULL) { return gateUsage(kUsage, GATE_COUNT_OF(kUsage)); } else { png = argv[arg]; } } if (png == NULL) { return gateUsage(kUsage, GATE_COUNT_OF(kUsage)); } gateImageLoad(png, &img); gateKeysPalette(words); gateRgb12(words[LIT_INDEX], lit); gateRgb12(words[UNLIT_INDEX], unlit); // Each cell's middle, on the screenshot. y = atY + (MARGIN_Y + ROW * (CELL_H + GAP) + CELL_H / 2) * scale; for (i = 0; i < COLUMN_COUNT; i++) { x = atX + (MARGIN_X + kColumns[i] * (CELL_W + GAP) + CELL_W / 2) * scale; if (!gateImageHas(&img, x, y)) { toolFail("cell %u's middle, %ld,%ld, is off the %ux%u screenshot", (unsigned)i, x, y, (unsigned)img.width, (unsigned)img.height); } pixel = gateImagePixel(&img, (uint32_t)x, (uint32_t)y); if (gateDistance(pixel, lit) <= MAX_DIST) { digits[i] = '1'; } else if (gateDistance(pixel, unlit) <= MAX_DIST) { digits[i] = '0'; } else { digits[i] = '?'; } } digits[COLUMN_COUNT] = '\0'; printf("%s\n", digits); gateImageFree(&img); return 0; }