Skip to content

Fix touch navbar after display orientation flip - #310

Open
oroderico wants to merge 2 commits into
Blockstream:masterfrom
oroderico:fix-ws-touch-navbar-flip-clean
Open

Fix touch navbar after display orientation flip#310
oroderico wants to merge 2 commits into
Blockstream:masterfrom
oroderico:fix-ws-touch-navbar-flip-clean

Conversation

@oroderico

@oroderico oroderico commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

This fixes the touch navigation bar behavior when the display orientation is flipped on boards that use the Jade-drawn virtual navbar.

Previously, flipping the display orientation could leave the virtual navbar out of sync with the touch coordinates: the navbar needed to be redrawn in the new orientation, and touches in the navbar band needed to follow the flipped display coordinates.

This change:

  • extracts the virtual navbar drawing into a reusable redraw helper
  • redraws the virtual navbar after the display orientation changes
  • adds a shared DISPLAY_HAS_TOUCH_NAVBAR guard for boards with the Jade-drawn navbar
  • moves the touch navbar height into a shared TOUCH_BUTTON_AREA constant
  • extends the touch Y range to include the navbar area
  • mirrors the touch Y coordinate when the display is flipped
  • keeps the touch X coordinate unchanged, so the physical left/right navigation sides remain consistent

The navbar redraw and the touch mirroring apply to the boards that use the Jade-drawn touch navbar:

  • M5 CoreS3
  • TTGO T-Watch S3
  • Waveshare S3 Touch LCD2

M5 Core2 is excluded from those two: its buttons are fixed capacitive hot zones below the display, not the Jade-drawn virtual navbar, so they must not follow the flip.

The two touch changes (y_max and the navbar band threshold) do apply to all four touchscreen boards, since they describe the panel rather than the navbar. Replacing the hardcoded touch_y[0] > 200 with the board display height also fixes a pre-existing bug on the M5 Core2, where taps on the bottom 40 pixels of the GUI area triggered navigation. Neither expression includes CONFIG_DISPLAY_OFFSET_Y: that is the ST7789 GRAM gap fed to esp_lcd_panel_set_gap(), a display-side value the touch controller never sees.

Testing

Tested on Waveshare S3 Touch LCD2:

  • default orientation: prev / OK / next work as expected
  • flipped orientation: navbar is redrawn correctly
  • flipped orientation: prev / OK / next remain on the expected physical sides
  • entered and exited the QR scanner flow with flipped orientation enabled

I do not have M5 CoreS3 or TTGO T-Watch S3 hardware to test directly. All four touchscreen targets (m5cores3, m5core2, ttgo_twatchs3, waveshares3_touch_lcd2) build clean against ESP-IDF v5.5.4.

Thanks to @cateim for helping investigate the touchscreen/navbar behavior and the orientation mirroring approach.

On boards with a virtual touch navbar (TTGO TWatchS3, M5 CoreS3,
Waveshare S3 Touch LCD 2) the prev/OK/next buttons are drawn only once
at startup, inside display_init(). Flipping the display orientation
remaps the panel scan direction, so the navbar was left stale and
upside down at the wrong edge of the panel.

Extract the drawing code into display_touch_navbar_redraw() and call
it from gui_set_flipped_orientation() whenever the orientation
actually changes. Boards without a navbar get an inline no-op stub.
The TOUCH_BUTTON_AREA constant moves to display.h so the touch input
code can share it. The two 50 ms settle delays stay in display_init(),
as flipping does not need them.

Co-authored-by: Gustavo Cateim <cateim@gmail.com>
@oroderico

Copy link
Copy Markdown
Contributor Author
flip_demonstration.mp4

@oroderico
oroderico marked this pull request as ready for review July 12, 2026 02:26
@cateim

cateim commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

I went through this change board by board before pinging for review, since I only have Waveshare hardware. Summary: the approach is sound and it also fixes a latent bug on the M5 Core2, but there is one regression on the TTGO T-Watch S3 that needs a 2-line fix first.

Board-by-board analysis

The navbar redraw and the touch mirroring are behind DISPLAY_HAS_TOUCH_NAVBAR, but the two changes in main/input/touchscreen.inc (y_max and the navbar band threshold) are outside that guard, so they reach all four touchscreen boards. Replacing the hardcoded touch_y[0] > 200 with CONFIG_DISPLAY_HEIGHT + CONFIG_DISPLAY_OFFSET_Y gives:

Board Panel Touch Y range Navbar band New threshold Result
M5 CoreS3 320x240 0..239 on-screen, 200..240 200 unchanged
M5 Core2 320x240 0..279 (capacitive strip below display) off-screen, 240..280 240 fixes a real bug: taps on the bottom 40px of the GUI currently trigger navigation
Waveshare S3 Touch LCD 2 240x320 0..319 on-screen, 280..320 280 the fix, tested on hardware both orientations
TTGO T-Watch S3 240x240 0..239 on-screen, 200..240 280 regression: threshold above the touch range, navbar becomes unreachable

Root cause of the T-Watch S3 regression

CONFIG_DISPLAY_OFFSET_Y is not panel geometry. It is the ST7789 GRAM gap: the controller has 240x320 of RAM and the T-Watch's 240px panel maps to rows 80..319, which is why the board sets CONFIG_DISPLAY_OFFSET_Y=80. The value is fed to esp_lcd_panel_set_gap() in main/display_hw.c:213, a display-side API. The FT6336 touch controller reports panel coordinates 0..239 and never sees that offset, so adding it to the touch threshold mixes two coordinate spaces. It goes unnoticed on the other three boards only because their offset is 0.

One trap for anyone testing this on a T-Watch S3: the flipped orientation still works (the software mirror y' = 320 - y lands the top band above 280), it is the default orientation that breaks. A flipped-only test would hide the regression.

Suggested fix

Drop CONFIG_DISPLAY_OFFSET_Y from both expressions in main/input/touchscreen.inc:

-        .y_max = CONFIG_DISPLAY_HEIGHT + CONFIG_DISPLAY_OFFSET_Y + TOUCH_BUTTON_AREA,
+        .y_max = CONFIG_DISPLAY_HEIGHT + TOUCH_BUTTON_AREA,
-                if (touch_y[0] > CONFIG_DISPLAY_HEIGHT + CONFIG_DISPLAY_OFFSET_Y) {
+                if (touch_y[0] > CONFIG_DISPLAY_HEIGHT) {

With that, y_max becomes 240 / 280 / 320 / 240, which matches the physical touch range of each board exactly (the Core2's FT6336 really does report a 320x280 area, its printed button strip sits at Y 240..280, exactly DISPLAY_HEIGHT..DISPLAY_HEIGHT+TOUCH_BUTTON_AREA). The threshold becomes 200 / 240 / 280 / 200, and the T-Watch mirror math still works flipped: 240 - (0..40) = 200..240 > 200.

@oroderico if you agree, can you apply this to the branch? I can also send it as a suggestion on the diff lines if you prefer.

@jgriffiths once that lands, what else would you want to see before this can be reviewed? Waveshare S3 Touch LCD 2 is tested on hardware both ways, but neither of us has the M5 or TTGO boards, and CI currently builds none of the four touchscreen targets. We can post build output for m5cores3, m5core2, ttgo_twatchs3 and waveshares3_touch_lcd2 if that helps.

@jgriffiths

Copy link
Copy Markdown
Collaborator

what else would you want to see before this can be reviewed?

@cateim @oroderico Once you are both happy the changes are complete, I will review and merge.

Note that 1.0.41 changes will be landing in master later today, and we will likely then push most of the current WIP 1.0.42 branch so that AI reviewers can stop sending us duplicate reports. So, I'll ask you to rebase on and target the 1.0.42 branch once that lands.

@cateim

cateim commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Tested the fix on top of this PR on a Waveshare S3 Touch LCD 2: navbar works in both orientations, QR scanner flow ok, no regressions. @oroderico once the two lines are on your branch I'm happy on my side. Noted on retargeting to 1.0.42.

The touch controllers keep reporting physical panel coordinates after the display orientation is flipped, so the virtual navbar band stops lining up with the rendered buttons: taps still land on the stale Y band.

Mirror only the touch Y coordinate from the touchscreen task whenever the GUI orientation changes. This keeps the navbar hit area in the same coordinate space as the display while preserving the physical left/right button actions. Mirroring X would make the right-side button trigger prev after a flip.

The M5 Core2 is excluded: its buttons are silk-screened on a fixed capacitive strip below the display and must not follow the flip.

For the software mirror to be correct, y_max must span the full touch panel, including the navbar area below the main display, so it gains TOUCH_BUTTON_AREA. The navbar band check also replaces the hardcoded touch_y > 200 with the board display height: on displays taller than 200 pixels, such as the Waveshare S3 Touch LCD 2 (280 pixels), the old value made taps on the bottom of the GUI area trigger navigation.

Neither expression includes CONFIG_DISPLAY_OFFSET_Y. That value is not panel geometry: it is the ST7789 GRAM gap fed to esp_lcd_panel_set_gap(), a display-side API. The touch controller reports panel coordinates and never sees it, so adding it here would mix two coordinate spaces. It is 0 on three of the four touchscreen boards, but 80 on the TTGO TWatchS3, where it would push the navbar threshold to 280 on a panel that only reports 0..239 and make the navbar unreachable in the default orientation.

Co-authored-by: oroderico <oroderico@users.noreply.github.com>
@oroderico
oroderico force-pushed the fix-ws-touch-navbar-flip-clean branch from 121e93d to 87b5751 Compare August 25, 2026 00:37
@oroderico

Copy link
Copy Markdown
Contributor Author

@cateim Done — both lines are in, amended into 87b5751f, so you'll need to re-fetch.

Ran the numbers for all four boards off Kconfig.projbuild before/after. Confirms what you said: the T-Watch navbar was dead in the default orientation (threshold 280 on a panel that only reports 0..239) and worked flipped. After the fix all four are fine both ways, and CoreS3/Waveshare are byte-identical either way — no-op there.

Also put a note in the commit message on why the offset doesn't belong in those expressions, since it's 0 on three of the four boards and looks harmless.

Built all four in the pinned jade_builder image (IDF 5.5.4) since CI doesn't build any of them:

m5cores3               OK
m5core2                OK
ttgo_twatchs3          OK
waveshares3_touch_lcd2 OK

Didn't reflash — the delta over what you tested is just your two lines, and they don't change anything on the Waveshare.

@jgriffiths good from our side. Will rebase onto 1.0.42 when it's up.

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.

3 participants