Files
MeshCore-Solo/examples/companion_radio/MyMeshScreenshot.h
T

83 lines
3.3 KiB
C
Raw Normal View History

#pragma once
// Screenshots over the companion protocol (ENABLE_SCREENSHOT).
// Not part of upstream MyMesh.cpp: included at its bottom, after all class
// definitions (as MyMeshBot.h and MyMeshCli.h).
#ifdef ENABLE_SCREENSHOT
void MyMesh::handleScreenshotRequest() {
#ifdef DISPLAY_CLASS
extern UITask ui_task; // main.cpp -- the Listener may be the UI Core, not UITask
#ifdef UI_SCREENSHOT_RGB565
// A colour UI renders a frame for it (RGB565, display type 2).
int w, h;
uint16_t* px = ui_task.captureFrame(w, h);
if (!px) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
return;
}
sendScreenshotResponse(SCREENSHOT_TYPE_RGB565, 0, (uint16_t)w, (uint16_t)h, (const uint8_t*)px, (uint32_t)w * h * 2);
free(px);
#else
if (!ui_task.hasDisplay()) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
return;
}
DisplayDriver* display = ui_task.getDisplay();
if (!display) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
return;
}
const uint8_t* buffer = display->getBuffer();
uint16_t bufferSize = display->getBufferSize();
if (buffer && bufferSize > 0) {
sendScreenshotResponse(display->getDisplayType(), display->screenshotRotation(),
(uint16_t)display->screenshotWidth(), (uint16_t)display->screenshotHeight(),
buffer, bufferSize);
} else {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
}
#endif
#else
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
#endif
}
void MyMesh::sendScreenshotResponse(uint8_t displayType, uint8_t rotation, uint16_t width, uint16_t height,
const uint8_t* buffer, uint32_t bufferSize) {
// Frame format (11-byte header, little-endian multi-byte fields):
// [0] resp_code = RESP_CODE_SCREENSHOT
// [1] display_type (0=OLED page-based, 1=e-ink row-major MSB-first 1=white,
// 2=RGB565 little-endian, row-major -- colour screens)
// [2] rotation (0-3, GxEPD2/Adafruit_GFX value; only meaningful for e-ink)
// [3..4] width (uint16 LE — GxEPD2-reported visible width)
// [5..6] height (uint16 LE — GxEPD2-reported visible height)
// [7..8] chunk_idx (uint16 LE)
// [9..10] total_chunks (uint16 LE)
// [11..] chunk data
const int HEADER_SIZE = 11;
const uint32_t MAX_DATA_PER_FRAME = MAX_FRAME_SIZE - HEADER_SIZE;
const uint16_t totalChunks = (uint16_t)((bufferSize + MAX_DATA_PER_FRAME - 1) / MAX_DATA_PER_FRAME);
for (uint16_t chunkIdx = 0; chunkIdx < totalChunks; chunkIdx++) {
int i = 0;
out_frame[i++] = RESP_CODE_SCREENSHOT;
out_frame[i++] = displayType;
out_frame[i++] = rotation;
out_frame[i++] = width & 0xFF; out_frame[i++] = width >> 8;
out_frame[i++] = height & 0xFF; out_frame[i++] = height >> 8;
out_frame[i++] = chunkIdx & 0xFF; out_frame[i++] = chunkIdx >> 8;
out_frame[i++] = totalChunks & 0xFF; out_frame[i++] = totalChunks >> 8;
const uint32_t offset = (uint32_t)chunkIdx * MAX_DATA_PER_FRAME;
const uint32_t chunkSize = min(MAX_DATA_PER_FRAME, bufferSize - offset);
memcpy(&out_frame[i], buffer + offset, chunkSize);
i += chunkSize;
_serial->writeFrame(out_frame, i);
}
}
#endif // ENABLE_SCREENSHOT