Skip to content

Commit e2a2cd9

Browse files
committed
android: Workaround display refresh bug
Whenever the screen has a non-zero offset, there is a display refresh bug that shows up on the simulator, where the leftmost pixels of the screen are refreshed with a delay. This seems to be a compositor bug, but the only workaround I found after half a day of trying was to have a larger pixmap that covers the whole screen width. Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
1 parent 117a9df commit e2a2cd9

3 files changed

Lines changed: 83 additions & 14 deletions

File tree

sim/sim-screen.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ SimScreen::SimScreen(QWidget *parent)
8080
bgPen(bgColor),
8181
fgPen(fgColor),
8282
mainPixmap(SIM_LCD_W, SIM_LCD_H),
83+
pixmapWidth(SIM_LCD_W),
84+
contentXOffset(0),
8385
redraws(0)
8486
{
8587
screen.clear();
@@ -112,6 +114,45 @@ SimScreen::~SimScreen()
112114
}
113115

114116

117+
void SimScreen::setPixmapGeometry(int totalWidth, int xOffset)
118+
// ----------------------------------------------------------------------------
119+
// Set the pixmap geometry for Android (wider pixmap with black bars)
120+
// ----------------------------------------------------------------------------
121+
{
122+
// Ensure pixmap is wide enough to contain LCD content and black bars
123+
int requiredWidth = xOffset + SIM_LCD_W;
124+
if (totalWidth < requiredWidth)
125+
totalWidth = requiredWidth;
126+
127+
if (totalWidth != pixmapWidth || xOffset != contentXOffset)
128+
{
129+
pixmapWidth = totalWidth;
130+
contentXOffset = xOffset;
131+
132+
// Create a new wider pixmap filled with black bars
133+
QPixmap newPixmap(pixmapWidth, SIM_LCD_H);
134+
newPixmap.fill(Qt::black);
135+
136+
// Draw the LCD content area with the background color
137+
QPainter painter(&newPixmap);
138+
painter.fillRect(contentXOffset, 0, SIM_LCD_W, SIM_LCD_H, bgColor);
139+
painter.end();
140+
141+
// Replace the pixmap
142+
mainPixmap = newPixmap;
143+
mainScreen->setPixmap(mainPixmap);
144+
145+
// Update scene rect to match new pixmap width
146+
setSceneRect(0, 0, pixmapWidth, screen_height);
147+
centerOn(qreal(pixmapWidth) / 2, qreal(screen_height) / 2);
148+
149+
// Force redraw of all LCD content
150+
for (size_t i = 0; i < sizeof(lcd_copy) / sizeof(*lcd_copy); i++)
151+
lcd_copy[i] = ~lcd_buffer[i];
152+
}
153+
}
154+
155+
115156
void SimScreen::setScale(qreal sf)
116157
// ----------------------------------------------------------------------------
117158
// Adjust the scaling factor
@@ -135,6 +176,10 @@ void SimScreen::updatePixmap()
135176
{
136177
// Monochrome screen
137178
QPainter pt(&mainPixmap);
179+
#ifdef ANDROID
180+
// On Android, set clip rect to only allow drawing in the LCD content area
181+
pt.setClipRect(contentXOffset, 0, SIM_LCD_W, SIM_LCD_H);
182+
#endif
138183
pixword mask = ~(~0U << color::BPP);
139184
surface s(lcd_buffer, LCD_W, LCD_H, LCD_SCANLINE, LCD_W);
140185
for (int y = 0; y < SIM_LCD_H; y++)
@@ -161,7 +206,11 @@ void SimScreen::updatePixmap()
161206
coord yy = y;
162207
s.horizontal_adjust(xx, xx);
163208
s.vertical_adjust(yy, yy);
209+
#ifdef ANDROID
210+
pt.drawPoint(xx + contentXOffset, yy);
211+
#else
164212
pt.drawPoint(xx, yy);
213+
#endif
165214
}
166215
}
167216
lcd_copy[woffs] = lcd_buffer[woffs];

sim/sim-screen.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,24 @@ class SimScreen : public QGraphicsView
5757
QGraphicsPixmapItem *mainScreen;
5858
QPixmap mainPixmap;
5959

60+
uint pixmapWidth;
61+
uint contentXOffset;
6062
uint redraws;
6163

6264
static SimScreen *theScreen;
6365

64-
public:
66+
public:
6567
explicit SimScreen(QWidget *parent = 0);
6668
~SimScreen();
6769

68-
public:
70+
public:
6971
void setScale(qreal _scale);
7072
void updatePixmap();
7173
void refreshScreen();
7274
static void update_pixmap() { theScreen->updatePixmap(); }
7375
static void refresh_lcd() { theScreen->refreshScreen(); }
7476
static uint redraw_count() { return theScreen->redraws; }
77+
void setPixmapGeometry(int totalWidth, int xOffset);
7578
};
7679

7780
#endif // WASM

sim/sim-window.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,29 +273,46 @@ void MainWindow::resizeEvent(QResizeEvent * event)
273273
kh = keyboard_height * kr;
274274
}
275275

276-
// Set screen ratio and geometry (full width, at top)
277-
int x = 0;
278-
int y = 0;
276+
// Set screen ratio and geometry
277+
int xOffset = (nw - scaledSW) / 2;
278+
int yOffset = (nh - scaledSH - kh) / 2;
279+
int screenWidth = scaledSW;
280+
int screenHeight = scaledSH;
281+
279282
#ifdef ANDROID
283+
// On Android: use a wider pixmap with black bars and position at x=0
284+
// This is because the primary screen needs to be at x=0, otherwise
285+
// we observe really weird refresh delays on the leftmost columns
280286
QScreen *screen = QGuiApplication::primaryScreen();
281287
if (screen)
282288
{
283-
// Or get the available geometry (excludes system UI like status bar)
284289
QRect availableGeometry = screen->availableGeometry();
285290
int availableWidth = availableGeometry.width();
286-
int availableHeight = availableGeometry.height();
287-
x = (availableWidth - nw) / 2;
288-
y = (availableHeight - nh) / 2;
291+
xOffset = (availableWidth - nw) / 2;
292+
293+
// Calculate where LCD content should be drawn within the pixmap
294+
int unscaledPixmapWidth = availableWidth / sr;
295+
int unscaledContentX = (xOffset + (nw - scaledSW) / 2) / sr;
296+
297+
// Ensure pixmap is wide enough
298+
int requiredPixmapWidth = unscaledContentX + ui.screen->screen_width;
299+
if (unscaledPixmapWidth < requiredPixmapWidth)
300+
unscaledPixmapWidth = requiredPixmapWidth;
301+
302+
// Set the pixmap to span the full width with black bars
303+
ui.screen->setPixmapGeometry(unscaledPixmapWidth, unscaledContentX);
304+
screenWidth = unscaledPixmapWidth * sr;
305+
xOffset = 0;
306+
nw = availableWidth;
289307
}
290-
#endif
308+
#endif // ANDROID
291309

292-
QRect sframe(x + (nw - scaledSW) / 2, y, scaledSW, scaledSH);
310+
// Position the screen view (always at x=0 on Android)
311+
QRect sframe(xOffset, yOffset, screenWidth, screenHeight);
293312
ui.screen->setGeometry(sframe);
294313
ui.screen->setScale(sr);
295314

296-
// Set keyboard size (centered horizontally, with spacing from screen)
297-
qreal spacing = (nh - scaledSH - kh) / 2;
298-
QRect kframe(x + (nw - kw) / 2, y + scaledSH + spacing, kw, kh);
315+
QRect kframe((nw - kw) / 2, yOffset + screenHeight, kw, kh);
299316
ui.keyboard->setGeometry(kframe);
300317
}
301318

0 commit comments

Comments
 (0)