Skip to content

feat: Take screenshots#759

Merged
daveallie merged 45 commits intocrosspoint-reader:masterfrom
el:screenshot
Feb 22, 2026
Merged

feat: Take screenshots#759
daveallie merged 45 commits intocrosspoint-reader:masterfrom
el:screenshot

Conversation

@el
Copy link
Contributor

@el el commented Feb 8, 2026

Summary

  • What is the goal of this PR? Implements a take-screenshot feature
  • What changes are included?
  • Quick press Power button and Down button at the same time to take a screenshot
  • Screenshots are saved in screenshots folder

Additional Context

  • Currently it does not use the device orientation.

Example screenshots:

screenshot-6771.bmp
screenshot-6771.bmp

screenshot-14158.bmp
screenshot-14158.bmp

AI Usage

Did you use AI tools to help write this code? _** YES

@CaptainFrito
Copy link
Contributor

Very cool idea, would it be a good idea to add a visual indication that a screenshot was taken?

@el
Copy link
Contributor Author

el commented Feb 8, 2026

Very cool idea, would it be a good idea to add a visual indication that a screenshot was taken?

Done

@CaptainFrito
Copy link
Contributor

Hey @el, sorry to nitpick but if you have the setting "Short power button click" set to "sleep", when trying to take screenshots the device goes to sleep if you're not careful to press both buttons exactly at the same time. Not sure that's a big deal, I still managed to take a few screenshots.


for (int y = 0; y < height; y++) {
const uint8_t* fbRow = framebuffer + (height - 1 - y) * fbRowSize;
if (file.write(fbRow, fbRowSize) != fbRowSize) {
Copy link
Contributor

@ngxson ngxson Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An extra idea: you can rotate the image on-the-fly by allocating a row of pixels on the stack, then fill it.

The shorter edge of the screen is 480 pixels, with 1bpp that means 60 bytes to be allocated, we should always have enough memory for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you mind if I submit a patch here to rotate the bitmap on-the-fly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not at all, feel free to update

@Eloren1
Copy link
Contributor

Eloren1 commented Feb 9, 2026

Can you please add Take screenshot button to a quick reader menu as well? (hold ok to open)

Copy link
Contributor

@bramschulting bramschulting left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super cool!

src/main.cpp Outdated
return;
}

String filename_str = "/screenshots/screenshot-" + String(millis()) + ".bmp";
Copy link
Contributor

@bramschulting bramschulting Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point millis() wraps around. This results in the (incredibly small) chance of the same name being used twice, but more importantly the screenshots will potentially be out of order. Perhaps we can store the last 'screenshot number' as a uint16 in CrossPointState? That would give room for 65,535 screenshots before this becomes a problem.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would give room for 65,535 screenshots before this becomes a problem.

wrapped screenshot number + millis = infinite amount of screenshots

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have used date+time but x4 doesnt have an rtc :)

I think this is enough for now

@el
Copy link
Contributor Author

el commented Feb 9, 2026

Can you please add Take screenshot button to a quick reader menu as well? (hold ok to open)

I dont see how it would be useful, you mean take a screenshot after closing the menu?

If so, I think we can do that after this is merged.


for (int y = 0; y < height; y++) {
const uint8_t* fbRow = framebuffer + (height - 1 - y) * fbRowSize;
if (file.write(fbRow, fbRowSize) != fbRowSize) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you mind if I submit a patch here to rotate the bitmap on-the-fly?

@Eloren1
Copy link
Contributor

Eloren1 commented Feb 9, 2026

Can you please add Take screenshot button to a quick reader menu as well? (hold ok to open)

I dont see how it would be useful, you mean take a screenshot after closing the menu?

If so, I think we can do that after this is merged.

Yes after closing. So it is possible to do one handed. And more noticeable

ngxson
ngxson previously approved these changes Feb 9, 2026
Copy link
Contributor

@ngxson ngxson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the 90d ccw rotation in the last commit, should work fine now.

I think fixed rotation should already be enough for now, as most the time we use the default portrait orientation.

One thing to note: it would be nice if we can download the image from web UI. Currently I need to take out the SD card to get the image.

@ngxson ngxson requested a review from a team February 11, 2026 17:35
@osteotek
Copy link
Member

I’ve been testing this and found that setting the “Short power button click” to “sleep” actually conflicts with the combination for taking screenshots. And overall, I believe the current input manager isn’t very well-suited for reading multiple key combinations at once. Perhaps it would be better to add an action for taking screenshots to the reader menu?

@el
Copy link
Contributor Author

el commented Feb 13, 2026

I’ve been testing this and found that setting the “Short power button click” to “sleep” actually conflicts with the combination for taking screenshots. And overall, I believe the current input manager isn’t very well-suited for reading multiple key combinations at once. Perhaps it would be better to add an action for taking screenshots to the reader menu?

@Eloren1 has also suggested that change. I have implemented the Take Screenshot to the menu and also moved all logic to a new util.

I have also increased sleep duration slightly to avoid conflicts.

@osteotek
Copy link
Member

osteotek commented Feb 13, 2026

Thank you for the changes! I guess my question is, do we really need button combination for taking screenshots? Now that we have menu option, I am not sure it is that much required, considering we have to change existing logic to properly support it. @crosspoint-reader/firmware-maintainers what do you think

@el
Copy link
Contributor Author

el commented Feb 13, 2026

Thank you for the changes! I guess my question is, do we really need button combination for taking screenshots? Now that we have menu option, I am not sure it is that much required, considering we have to change existing logic to properly support it. @crosspoint-reader/firmware-maintainers what do you think

I think it is still usefull, I imagine this feature could be used when

  • You want to share your home screen or recents
  • Someone creates a PR that affects the UI, they can share its screenshot.

@osteotek
Copy link
Member

osteotek commented Feb 13, 2026

Thank you for the changes! I guess my question is, do we really need button combination for taking screenshots? Now that we have menu option, I am not sure it is that much required, considering we have to change existing logic to properly support it. @crosspoint-reader/firmware-maintainers what do you think

I think it is still usefull, I imagine this feature could be used when

  • You want to share your home screen or recents
  • Someone creates a PR that affects the UI, they can share its screenshot.

Absolutely, I am not talking about screenshot feature in general, it is useful, thank you for this btw. I'm just not sure if I like button overloading. But let's wait for other maintainers opinion

@osteotek osteotek mentioned this pull request Feb 18, 2026
@ngxson
Copy link
Contributor

ngxson commented Feb 18, 2026

Personally I think it's still be useful for development and bug reports in general. The implementation introduced in this PR is easy enough, so I think it's worth merging into master.

Re. problem with key combination, one idea is to add a new dialog that will be shown when holding power button, and we can move the "screenshot" to that dialog menu. Quite the same idea as on android (see screenshot below). Probably worth discussing this as an UI feature.

image

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a screenshot feature: hotkey (Power + Volume Down) and reader-menu trigger capture the framebuffer, save a rotated 1-bit BMP to /screenshots/, and show a brief on-screen confirmation. Also adds BMP header utilities and translations for the new menu label.

Changes

Cohort / File(s) Summary
Screenshot utility
src/util/ScreenshotUtil.h, src/util/ScreenshotUtil.cpp
New ScreenshotUtil with takeScreenshot() and saveFramebufferAsBmp() — captures framebuffer, rotates 90° CCW, writes 1-bit BMP to /screenshots/screenshot-<ms>.bmp, handles directory/file I/O, errors, and draws a brief border confirmation.
Reader integration
src/activities/reader/EpubReaderActivity.h, src/activities/reader/EpubReaderActivity.cpp, src/activities/reader/EpubReaderMenuActivity.h
Adds MenuAction::SCREENSHOT, inserts menu item, adds pendingScreenshot flag and deferred-capture flow guarded by render mutex; menu confirm triggers screenshot after next render completes.
Input loop & settings
src/main.cpp, src/CrossPointSettings.h
Detects Power+VolumeDown combo in main loop with debounce and guard against auto-sleep; calls ScreenshotUtil. Adjusts power-button duration return value for SLEEP branch.
BMP support
lib/GfxRenderer/Bitmap.h, lib/GfxRenderer/BitmapHelpers.h, lib/GfxRenderer/BitmapHelpers.cpp
Adds packed BmpHeader (file/info headers), RgbQuad and colors[], extends BmpReaderError with multiple cases, and new createBmpHeader() to initialize 1-bit BMP headers/colour table and sizes.
I18n / Translations
lib/I18n/I18nKeys.h, lib/I18n/translations/english.yaml, .../czech.yaml, .../french.yaml, .../german.yaml, .../portuguese.yaml, .../russian.yaml, .../spanish.yaml, .../swedish.yaml
Adds new string id STR_SCREENSHOT_BUTTON and provides translations in eight languages.
Docs
USER_GUIDE.md
Documents screenshot methods: Power+VolumeDown hotkey and reader-menu "Take screenshot" (via Confirm in reader menu).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Main as main.cpp
    participant ScreenshotUtil as ScreenshotUtil
    participant Gfx as GfxRenderer
    participant Storage as Storage

    User->>Main: Press Power + Volume Down
    Main->>ScreenshotUtil: takeScreenshot(renderer)
    ScreenshotUtil->>Gfx: request framebuffer
    Gfx-->>ScreenshotUtil: framebuffer bits
    ScreenshotUtil->>Storage: ensure /screenshots/ exists
    ScreenshotUtil->>ScreenshotUtil: rotate 90° CCW & build BMP header
    ScreenshotUtil->>Storage: write BMP header + pixel data
    ScreenshotUtil->>Gfx: draw temporary border confirmation
    Storage-->>ScreenshotUtil: write success/failure
    ScreenshotUtil-->>Main: return result
Loading
sequenceDiagram
    actor User
    participant Menu as EpubReaderMenuActivity
    participant Reader as EpubReaderActivity
    participant Renderer as GfxRenderer
    participant ScreenshotUtil as ScreenshotUtil
    participant Storage as Storage

    User->>Menu: Open reader menu & select "Take screenshot"
    Menu->>Reader: MenuAction::SCREENSHOT
    Reader->>Reader: set pendingScreenshot
    Reader->>Renderer: complete current render (holds render mutex)
    Reader->>Reader: detect pendingScreenshot, clear flag
    Reader->>ScreenshotUtil: takeScreenshot(renderer)
    ScreenshotUtil->>Renderer: get framebuffer
    ScreenshotUtil->>Storage: save BMP
    ScreenshotUtil->>Renderer: show confirmation border
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Take screenshots' clearly and concisely summarizes the main feature addition—screenshot functionality—which is the primary goal of the entire changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the goal (implementing screenshot feature), the trigger mechanism (Power + Down buttons), storage location (screenshots folder), and noting it's a work-in-progress regarding device orientation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@osteotek
Copy link
Member

@el if I press screenshot combination while page is still rendering I get crash with this error. Might be related to issue Copilot suggested

assert failed: xQueueGenericSend queue.c:832 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())

el and others added 2 commits February 20, 2026 13:42
@el el requested a review from ngxson February 20, 2026 15:14
ngxson
ngxson previously approved these changes Feb 20, 2026
@el el requested a review from osteotek February 20, 2026 16:05
@osteotek osteotek requested a review from ngxson February 20, 2026 18:16
@el
Copy link
Contributor Author

el commented Feb 20, 2026

@daveallie fyi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants