737c974400
- Split tests/ (libc feature tests) and examples/ (real apps); shared
app.mk in repo root, was examples/example.mk
- libc/io/* split into libc/{conio,env,errno,file,mouse,string,sys,
time,video}/ — clearer module boundaries
- New examples/mdview/: markdown viewer (Phases 1-5 + light nested
lists). Headers (H1-H4), HR, ulist/olist/quote with nesting via
leading spaces, fenced code blocks, inline emphasis (bold/italic/
underscore/code), wrap/unwrap mode with soft wrap (F2), horizontal
pan (← →) with '>' truncation indicator
- libc additions: scroll() in conio (ESTEX SCROLL), strlwr/strupr,
gets() test
- Makefile updates across tests/ for the new shared app.mk path
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
170 lines
5.3 KiB
C
170 lines
5.3 KiB
C
/*
|
||
* gfx_mouse_test — exercises the graphics-mode mouse cursor API:
|
||
* $09 LOAD CURSOR — install a custom cursor bitmap
|
||
* $81 CHANGE VIDEO MODE — let the driver re-sync after switching modes
|
||
* $0B RETURN CURSOR — read the cursor back (may be a stub in MAME)
|
||
*
|
||
* Flow:
|
||
* 1. Init the mouse driver in text mode.
|
||
* 2. Switch to 320x256x256, notify the driver.
|
||
* 3. Build an 8x8 cursor — solid white border + transparent middle —
|
||
* and load it. Show it. Move the mouse to verify it tracks.
|
||
* 4. Press a key: query the driver's view of the cursor and dump the
|
||
* result so we can compare against what we loaded.
|
||
* 5. Hide cursor, return to text mode, print the round-trip dump.
|
||
*
|
||
* The driver's bitmap format is undocumented — best guess is one byte
|
||
* per pixel (palette index, with 0 meaning transparent). We load 0xFF
|
||
* for opaque cells and 0 for transparent so the cursor stands out
|
||
* against any background.
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <conio.h>
|
||
#include <mouse.h>
|
||
#include <gfx.h>
|
||
#include <stdint.h>
|
||
|
||
#define CURSOR_W 12
|
||
#define CURSOR_H 12
|
||
#define CURSOR_BUF_BYTES (CURSOR_W * CURSOR_H)
|
||
|
||
/* Cursor format (verified empirically 2026-05-31):
|
||
* ONE BYTE per pixel — row-major, width*height bytes total.
|
||
* 0xFF = transparent (background shows through)
|
||
* any other byte = palette index (drawn opaque)
|
||
*
|
||
* Cursor bitmap lives in a dedicated video bank, not the 0x50 page used
|
||
* by gfx_clear256 / gfx_putpixel256 — that's why 0xFF reads as transparent
|
||
* even though in the main visible page 0xFF is a regular colour.
|
||
*/
|
||
|
||
static uint8_t cursor_bitmap[CURSOR_BUF_BYTES];
|
||
static uint8_t roundtrip_buf[CURSOR_BUF_BYTES];
|
||
static uint8_t palette[256 * 4];
|
||
|
||
/* Arrow shape, 1 byte per pixel. Each cell is either:
|
||
* _ transparent (0xFF, lets the background through)
|
||
* O black solid pixel (0x00)
|
||
* W white solid pixel (0xFF would mean transparent, so use 0xFF
|
||
* palette entry by hand if you want a white pixel — for the
|
||
* default greyscale palette in this demo, W = bright grey 0xE0)
|
||
*
|
||
* Feel free to edit the 12×12 grid below to draw a nicer cursor. */
|
||
#define _ 0xFF
|
||
#define O 0x00
|
||
#define W 0xE0
|
||
static const uint8_t cursor_arrow[CURSOR_BUF_BYTES] = {
|
||
O,_,_,_,_,_,_,_,_,_,_,_,
|
||
O,O,_,_,_,_,_,_,_,_,_,_,
|
||
O,W,O,_,_,_,_,_,_,_,_,_,
|
||
O,W,W,O,_,_,_,_,_,_,_,_,
|
||
O,W,W,W,O,_,_,_,_,_,_,_,
|
||
O,W,W,W,W,O,_,_,_,_,_,_,
|
||
O,W,W,W,W,W,O,_,_,_,_,_,
|
||
O,W,W,W,O,O,O,O,_,_,_,_,
|
||
O,O,O,W,O,_,_,_,_,_,_,_,
|
||
O,_,_,O,W,O,_,_,_,_,_,_,
|
||
_,_,_,O,W,O,_,_,_,_,_,_,
|
||
_,_,_,_,O,O,_,_,_,_,_,_,
|
||
};
|
||
#undef _
|
||
#undef O
|
||
#undef W
|
||
|
||
static void make_palette(void)
|
||
{
|
||
for (int i = 0; i < 256; i++) {
|
||
palette[i*4+0] = (uint8_t)i;
|
||
palette[i*4+1] = (uint8_t)i;
|
||
palette[i*4+2] = (uint8_t)i;
|
||
palette[i*4+3] = 0;
|
||
}
|
||
}
|
||
|
||
/* Copy the arrow pattern into our scratch (one place for the driver
|
||
* to point IX at). */
|
||
static void build_cursor(void)
|
||
{
|
||
for (int i = 0; i < CURSOR_BUF_BYTES; i++)
|
||
cursor_bitmap[i] = cursor_arrow[i];
|
||
}
|
||
|
||
static void wait_key(void)
|
||
{
|
||
while (!kbhit()) { /* spin */ }
|
||
(void)getch();
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||
clrscr();
|
||
cputs("gfx_mouse_test\r\n");
|
||
|
||
if (mouse_init() < 0) {
|
||
cputs("mouse_init failed - no driver\r\n");
|
||
return 1;
|
||
}
|
||
mouse_set_sensitivity(2, 2);
|
||
|
||
/* --- enter graphics, load custom cursor, then show -------------- */
|
||
make_palette();
|
||
build_cursor();
|
||
|
||
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
|
||
gfx_pal_load(0, 0, 0, palette);
|
||
gfx_clear256(0x40); /* dim grey background */
|
||
|
||
/* Coordinate grid every 32 px so the cursor position reads easily. */
|
||
for (int x = 0; x < GFX_WIDTH; x += 32) gfx_vline256(x, 0, GFX_HEIGHT, 0x80);
|
||
for (int y = 0; y < GFX_HEIGHT; y += 32) gfx_hline256(0, y, GFX_WIDTH, 0x80);
|
||
|
||
mouse_video_mode_changed(GFX_MODE_320x256x256);
|
||
mouse_bounds_x(0, GFX_WIDTH - 1);
|
||
mouse_bounds_y(0, GFX_HEIGHT - 1);
|
||
|
||
mouse_cursor_t custom = {
|
||
.image = cursor_bitmap,
|
||
.width = CURSOR_W,
|
||
.height = CURSOR_H,
|
||
.hot_x = 0,
|
||
.hot_y = 0,
|
||
};
|
||
mouse_load_cursor(&custom);
|
||
mouse_show();
|
||
wait_key();
|
||
|
||
/* --- query driver to see what it has now ------------------------ */
|
||
mouse_cursor_t readback = {
|
||
.image = roundtrip_buf,
|
||
.width = 0, .height = 0, .hot_x = 0, .hot_y = 0,
|
||
};
|
||
mouse_get_cursor(&readback);
|
||
|
||
mouse_hide();
|
||
gfx_done(prev);
|
||
mouse_video_mode_changed(prev); /* tell driver we're back */
|
||
|
||
/* --- back in text mode: report what the driver returned --------- */
|
||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||
clrscr();
|
||
cprintf("loaded : %ux%u hot (%u,%u)\r\n",
|
||
CURSOR_W, CURSOR_H, 0, 0);
|
||
cprintf("read back: %ux%u hot (%u,%u)\r\n",
|
||
readback.width, readback.height, readback.hot_x, readback.hot_y);
|
||
|
||
uint16_t n = (uint16_t)readback.width * (uint16_t)readback.height;
|
||
if (n > CURSOR_BUF_BYTES) n = CURSOR_BUF_BYTES;
|
||
cprintf("bitmap (%u bytes):\r\n", n);
|
||
for (uint16_t i = 0; i < n; i++) {
|
||
cprintf("%02X ", roundtrip_buf[i]);
|
||
if ((i % readback.width) == readback.width - 1) cputs("\r\n");
|
||
}
|
||
cputs("\r\n");
|
||
|
||
cputs("press any key to exit");
|
||
wait_key();
|
||
return 0;
|
||
}
|